什么是Java面向对象(实例详解)

什么是面向对象?来看集合篇,看完你就了解什么是面向对象

目录

  • 前言
  • 一、面向对象(基础篇)
    • 1. 面向对象基础
    • 1.1 面向对象思想
      • 1.1.1 概述
      • 1.1.2 三大思想
      • 1.1.3 三大特征
    • 1.2 类与对象
      • 1.2.1 两者的关系
      • 1.2.2 类的定义格式
      • 1.2.3 属性与方法
      • 1.2.4 对象的创建与使用
    • 1.3 构造方法
      • 1.3.1 概述
      • 1.3.2 构造方法的格式
      • 1.3.3 构造方法设计
    • 1.4 方法的重载
    • 1.5 构造方法的重载
    • 1.6 匿名对象
  • 二、面向对象(进阶篇)
    • 2 面向对象进阶
    • 2.1 封装 private
    • 2.2 this
    • 2.3 static
      • 2.3.1 概述
    • 2.4 代码块
      • 2.4.1 普通代码块
      • 2.4.2 构造代码块
      • 2.4.3 静态代码块
    • 2.5 包
      • 2.5.1 包介绍
      • 2.5.2 包的命名规则
      • 2.5.3 import 关键字
    • 2.6 权限修饰符
  • 三、面向对象(高级篇)
    • 3 面向对象高级
    • 3.1 继承
    • 3.2 super
    • 3.3 重写
    • 3.4 final
    • 3.5 抽象类
      • 3.5.1 概念
      • 3.5.2 抽象方法
      • 3.5.3 不能被实例化
      • 3.5.4 常见问题
      • 3.5.5 抽象类和普通类的区别
    • 3.6 接口
      • 3.6.1 概念
      • 3.6.2 面向接口编程思想
      • 3.6.3 全局常量和抽象方法的简写
      • 3.6.4 接口的实现
      • 3.6.5 接口的继承
      • 3.6.6 接口和抽象类的区别
    • 3.7 多态
      • 3.7.1 多态的体现
      • 3.7.2 多态的使用
      • 3.7.3 instanceof
    • 3.8 Object类
      • 3.8.1 概念
      • 3.8.2 Object的多态
      • 3.8.3 toString
      • 3.8.4 equals
    • 3.9 内部类
      • 3.9.1 概念
      • 3.9.2 成员内部类
      • 3.9.3 局部内部类
      • 3.9.4 匿名内部类
      • 3.9.5 静态内部类
    • 3.10 包装类
      • 3.10.1 概述
      • 3.10.2 装箱和拆箱操作
  • 总结


前言

学习了一周面向对象,一开始对于面向对象的概念我也觉得很抽象、很模糊,后来接触了一些项目后,才慢慢搞懂。下面是我整理出来关于面向对象的一些基础知识。

一、面向对象(基础篇)

1. 面向对象基础

Java是一种面向对象的编程语言。面向对象编程,英文是Object-Oriented Programming,简称OOP。

1.1 面向对象思想

1.1.1 概述

面向对象(Object Oriented)是相对面向过程(procedure Oriented)来说的,它们都是软件开发的一种方式。

面向对象的概念和应用已超越了程序设计和软件开发,是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。

那什么又是面向过程呢?

面向过程是关注流程的,每解决一个程序问题,都需要程序员逐步分析,然后再逐步实现。

举个例子:
小A饿了,需要吃饭。那么他就要自己去买菜、洗菜、淘米、煮饭、炒菜,最后吃饭,这是一个吃饭,解决饿的过程。在这个过程里呢,小A需要一步一步的去完成,最后才能吃到饭,解决饿的问题。这就是面向过程。

对于这种复杂的过程,面向对象就能更好的解决复杂问题。

面向对象是相对于面向过程来讲的,指的是把 相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。

举个例子:
小B饿了,需要吃饭。小B呢,不想自己做饭了,去饭店吃饭,解决饿的问题。小B不需要知道饭菜怎么做,只管好吃不好吃,能不能填饱肚子,这就是面向对象。

面向过程到面向对象思想层面的转变:

面向过程关注的是执行的过程,面向对象关注的是具备功能的对象。

面向过程到面向对象,是程序员思想上 从执行者到指挥者的转变。

1.1.2 三大思想

面向对象思想从概念上讲分为以下三种:OOA、OOD、OOP

  • OOA:面向对象分析(Object Oriented Analysis)

  • OOD:面向对象设计(Object Oriented Design)

  • OOP:面向对象程序(Object Oriented Programming)

1.1.3 三大特征

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

  • 封装性:所有的内容对外部不可见

  • 继承性:将其他的功能继承下来继续发展

  • 多态性:方法的重载本身就是一个多态性的体现

1.2 类与对象

1.2.1 两者的关系

类表示一个共性的产物,是一个综合的特征;
而对象,是一个个性的产物,是一个个体的特征。
(相当于设计图纸与实物的概念)
比如说建一栋房子,你得先有设计图吧,有了设计图,工人才能根据设计的内容进行建造。那么设计图纸就相当于编程里的类,工人就相当于编程里的对象。

类必须通过对象才能使用,对象所有操作都在类中定义。
类由属性和方法组成:

  • 属性:就相当于人的一个个的特征(如:勤劳、懒惰、忠诚)

  • 方法:就相当于人的一个个的行为(如:吃饭、睡觉、学习)

1.2.2 类的定义格式

类定义写在“ .java ”文件中
类的定义格式:

class 类名称{
成员属性
成员方法
}

一个“ .java ”文件中,可以存在N个类,但是只能存在一个public修饰的类
代码示例:

class Person{
    //属性 - 特征
    String name;//定义了一个字符串属性为name的特征
    int age;//定义了一个整型属性为age的特征
    char sex;//定义了一个字符型属性为sex的特征
}

一个

1.2.3 属性与方法

属性定义格式:

数据类型 属性名;

属性定义并赋值的格式:

数据类型 属性名 = 初始化值;

代码示例:

int age = 18;

方法定义格式:

返回值类型 方法名称(形式参数列表){
//方法体 :方法被调用时执行的代码
return 返回值;

关于方法:

1)返回值:方法体内向方法体外返回的数据内容,通过return关键字完成。如果一个方法一旦执行了return语句,就立即结束。返回值类型为void时,不用写return语句。
2) 返回值类型:返回值的数据类型,必须在方法定义时提前声明,可以是基本数据类型,也可以是引用型数据类型。如果不需要返回值,可以写void
3) 方法名称:需要给不同的方法赋予不同的名称。
4)形式参数列表:定义方法时,可以声明方法执行时所需的外部参数。参数可以多个,看具体方法需要。一个方法在定义时,还未被调用,所以是形式上的参数列表。参数格式:数据类型 名称 。当有多个参数时,用逗号隔开。
5)方法体:是方法中包含的多条程序语句。可以使用成员变量。

代码示例:

//返回类型为整型int 方法名为 sum 形式参数为整型a和b
int sum(int a,int b) {	
        int c = a + b;//方法体 对a和b求和,然后赋值给c
        return c;//返回值为整型int“ c ”
    }

1.2.4 对象的创建与使用

对象的创建格式如下:

类名称 对象名称 = new 类名称();

对象创建完,就可以使用对象的属性和方法了,使用格式如下:

给类中属性赋值:对象名.成员变量名称 = 值;

访问类中的属性:对象名.成员变量名称
调用类中的方法:对象名.方法(实际参数列表)

下面编写一个拥有颜色(color)、品牌(brand)和价格(price)属性,并包含介绍方法(introduction)的汽车类(Car)。
首先是定义类

//汽车类(Car)
class Car{
    //属性
    String color;   //颜色
    String brand;   //品牌
    //介绍的方法
    void show(){
        System.out.println("汽车的品牌为:"+brand);
        System.out.println("汽车的颜色为:"+color);
    }
}

定义类后,编写一个入口程序来创建一个汽车(对象)

public class Object_01 {
    public static void main(String[] args) {
        Car car = new Car();    //创建一个汽车对象,对象名为car
        car.color = "白色";      //给汽车car设置颜色color为"白色"
        car.brand = "宝马";      //给汽车car设置品牌brand为"宝马"
        //调用汽车car类show的方法
        car.show();
    }
}

程序运行结果:
在这里插入图片描述

1.3 构造方法

构造方法也叫构造器,顾名思义就是构造一个方法,用构造方法去构造对象,并给对象的属性进行赋值。

在下面代码右侧Person后面出现的小括号, 其实就是在调用构造方法 !

Person p = new Person();

1.3.1 概述

接下来说一下构造方法的作用、执行时机、特点等。
作用:用于对象初始化。
执行时机:在创建对象时,自动调用。
特点

  • 所有的java类中都会至少存在一个构造方法。

  • 如果一个类中没有明确的编写构造方法,则编译器会自动生成一个无参的构造方法,构造方法中没有任何的代码。

  • 如果自行编写了任意一个构造器,则编辑器不会再自动生成无参的构造方法。

1.3.2 构造方法的格式

构造方法的格式与普通方法基本相同,区别在于构造方法的方法名与类的名称完全相同,且构造方法无返回值类型的声明。格式如下:

修饰符 类名(参数列表){
//方法体
}

代码如下(示例):

public class Demo1 {
    public static void main(String[] args) {
        Person p = new Person();//打印一次
		Person p1 = new Person();//打印一次
		Person p2 = new Person();//打印一次
    }
}
//类名
class Person{
	//属性
    String name;
    int age;
	//无参数构造方法
    public Person(){
        System.out.println("创建对象时,就已经被调用了。");
    }
    //全属性有参数构造方法
    public Person(String name1, int age1) {
        name = name1;
        age = age1;
    }
}

1.3.3 构造方法设计

建议自定义无参构造方法,不要对编译器形成依赖,避免程序错误发生。

  • 当类中有非常量成员变量(属性)时,建议提供两个版本的构造方法,一个是无参构造方法,一个是全属性做参数的构造方法

  • 当类中所有成员变量都是常量或者没有成员变量时,建议不提供任何版本的构造。

1.4 方法的重载

一个类中定义的方法名称相同就是方法的重载
特点:

  • 1、方法名称相同。

  • 2、参数列表长度 或 参数类型 或 参数顺序不同。

  • 3、方法的重载与返回值无关。

  • 4、可以让程序在不同的需求下,通过传递不同的参数调用方法来完成某些具体的功能。

代码如下(示例):

public class Demo1 {

    public static void main(String[] args) {
        Math math = new Math();//创建对象
        int sum1 = math.sum(2, 3);//赋值int型数据
        double sum2 = math.sum(5.1,6.8);//赋值double型数据
        System.out.println(sum1);//5
        System.out.println(sum2);//11.899999999999999
    }
}
//创建类
class Math{
	//方法名为sum,参数类型为int
    int sum(int a,int b){
        int c = a + b;
        return c;
    }
    //方法名为sum,参数类型为double
    double sum(double a,double b){
        double c = a + b;
        return c;
    }
    //方法名相同,参数列表类型不同
    double sum(int b,double a){
        double c = a + b;
        return c;
    }
}

注意方法重载与返回值类型无关

1.5 构造方法的重载

构造方法的重载其实和方法的重载很像。
一个类,可以存在多个构造方法。
参数列表的长度或类型不同即可完成构造方法的重载。
可以在不同的创建对象需求下,调用不同的方法来完成对象的初始化。

代码如下(示例):

public class Demo2 {
    public static void main(String[] args) {
        Person2 p = new Person2("张三",20);
        p.show();//姓名:张三 年龄:20
        Person2 p1 = new Person2("李四");
        p1.show();//姓名:李四 年龄:0
    }
}
//类名为:Person2
class Person2{
	//属性
    String name;//姓名
    int age;//年龄
    //构造方法1(无参数形式)
    public Person2(){}
	//构造方法2(一个参数形式)
    public Person2(String name2) {
        name = name2;
    }
	//构造方法3(全属性参数形式)
    public Person2(String name2, int age2) {
        name = name2;
        age = age2;
    }
	//方法
    void show(){
        System.out.println("姓名:"+name+" 年龄:"+age);
    }
}

在类person2中,三个构造方法的参数类型或列表长度都不相同,但构造方法名称相同,这样的情况就是构造方法的重载。

1.6 匿名对象

顾名思义没有对象名称的对象,就是匿名对象

匿名对象只能使用一次,因为没有任何的对象引用,所以将称为垃圾,
等待被GC(垃圾回收器)回收。
只使用一次的对象可以通过匿名对象的方式完成。

代码如下(示例):

public class Demo3 {
    public static void main(String[] args) {
        //没有名字的对象,就是匿名对象
        new Person3("王五",25).show();
		//姓名:王五 年龄:25
    }
}

class Person3{
    String name;
    int age;

    public Person3(String name3, int age3) {
        name = name3;
        age = age3;
    }

    void show(){
        System.out.println("姓名:"+name+" 年龄:"+age);
    }
}

注意:如果一个对象要使用两次以上,就必须给创建对象赋值名称。


二、面向对象(进阶篇)

2 面向对象进阶

2.1 封装 private

概述:封装的意义在于保护或者防止数据被其它数据随意访问,导致程序无意被破坏。

在编写程序代码时,首先考虑哪些数据可以封装为一个单位,将属性和方法确定后统一协调使用,大大优化模块间的相互作用。

封装原则:隐藏对象的属性和实现细节,仅对外公开访问法,并且控制访问级别。

为了便于理解,观察如下代码:

//描述人的类Person2
class Person2{
    //属性
    String name;//姓名
    int age;//年龄

    //说的方法
    void say(){
        System.out.println("我是:"+name+" 年龄:"+age);
    }
}

上面的代码将Person2封装为一个整体。

将上述代码放到程序入口类
如下面代码:

public class Demo2 {
    public static void main(String[] args) {
        Person2 p = new Person2();
        p.name = "小红";
        p.age = -99;
        p.say();
    }
}

运行结果为:

我是:小红,年龄:-99岁。

进程已结束,退出代码 0

在上面这段代码中,整个流程下来程序是没有报错的,但是小红年龄-99岁,在现实中,显然是不合逻辑的。

那么针对这个问题,就可以选择用权限修饰符private(私有的)将类的”age“属性年龄私有化,也就是外部无法直接访问年龄。
再提供一些可供外部调用数据和验证数据的方法,外部相当于间接操作类的年龄,最终问题解决。

改进代码如下:

//描述人的类Person2
class Person2{
    //属性
    String name;//姓名
    //private修饰age,使其私有化,只有本类才能使用
    private int age;//年龄
    //可供外部调用的方式
    public void setAge(int age2) {
        //再通过传入的参数进行判断
        if (age2 < 0 || age2 > 150){
            age2 = 18;
            System.out.println("年龄设置不合理,已自动修正为默认值18。");
        }else {
            age = age2;
        }
    }
    //说的方法
    void say(){
        System.out.println("我是:"+name+",年龄:"+age+"岁。");
    }
}

程序入口:

public class Demo2 {
    public static void main(String[] args) {
        Person2 p = new Person2();
        p.name = "小明";
        //p.age = -99;//如果继续使用会报错
        p.setAge(-888);
        p.say();
    }
}

运行结果:

年龄设置不合理,已自动修正为默认值18。
我是:小明,年龄:18岁。

进程已结束,退出代码 0

提示:在以后的开发中,为了避免出现类似的逻辑错误,建议对所有属性进行封装,也就是加以private(私有的),并为其提供SetterGetter方法进行设置和取得数据操作。

2.2 this

在Java基础中,this关键字是一个最重要的概念。 使用this关键字可以完成一下操作:

  • 调用类中的属性

  • 调用类中的方法或构造方法

  • 表示当前对象

观察下面一段代码(示例):

public class Person{
	private int age;
	public void setAge(int age){
		age = age;
	}
}

在setAge方法体中,我们的想通过方法参数的age传进来赋值给属性age。
但是在执行过程中呢,其实是方法的参数age传给本身参数age,那么这段代码是没有意义的。
那么如何解决这个问题呢。那就是使用Java中的关键字this来表示当前对象,调用当前对象的属性。

添加this后代码如下(示例):

public class Person{
	private int age;
	public void setAge(int age){
		this.age = age;
	}
}

2.3 static

2.3.1 概述

static表示“静态”的意思,可以用来修饰成员变量和成员方法。
static的主要作用在与创建独立于具体对象的域变量或者方法。

通俗来说:
static关键字修饰的方法或者变量不需要依赖于对象来进行访问
只要类被加载了,就可以通过类名去进行访问
并且不会因为对象的多次创建,而在内存中建立多份数据。

代码如下(示例):

public class Demo3 {
    public static void main(String[] args) {
        Person p1 = new Person("小明","北京");
        Person p2 = new Person("小三","北京");
        Person p3 = new Person("小李","北京");
        Person p4 = new Person("小红","北京");
        p1.say();
        p2.say();
        p3.say();
        p4.say();
    }
}
class Person{
    String name;//姓名
    String place;//地方
    public Person(String name,String place) {
        this.place = place;
        this.name = name;
    }

    void say() {
        System.out.println("姓名:"+name+",居住:"+place);
    }
}

运行结果是这样的:

姓名:小明,居住:北京
姓名:小三,居住:北京
姓名:小李,居住:北京
姓名:小红,居住:北京

进程已结束,退出代码 0

观察代码就会发现,这些人都在“北京”这个地方,那么同一个地方就引用了4次,这显然会造成内存浪费。针对这个问题,就可以用static去解决了。

格式:

类名.静态成员变量 = 数值;

类名.静态方法名();

添加static后的代码(示例):

public class Demo3 {
    public static void main(String[] args) {
    	//不需要创建对象就能调用plac
        Person.place = "北京";
        Person p1 = new Person("小明");
        Person p2 = new Person("小三");
        Person p3 = new Person("小李");
        Person p4 = new Person("小红");
        p1.say();
        p2.say();
        p3.say();
        p4.say();
    }
}
class Person{
    private String name;//姓名
    //只要类被加载,就可以访问被static修饰的成员变量
    static String place;//地方
    public Person(String name,String place) {
        this.place = place;
        this.name = name;
    }
    public Person(String name){
        this.name = name;
    }

    void say() {
        System.out.println("姓名:"+name+",居住:"+place);
    }
}

static修饰方法名也是同理:

添加static后的方法代码(示例):

public class Demo3 {
    public static void main(String[] args) {
    	//不需要创建对象就能调用类的方法
      	Person.say();
    }
}
class Person{  
	//用static修饰方法名
    static void say() {
        System.out.println("床前明月光,疑是地上霜。");
    }
}

注意:

  1. 静态成员,在加载类时加载并初始化。

  2. 无论一个类存在多少个对象,对象的属性,在内存永远中只有一份

  3. 在调用时,静态不能访问非静态,非静态可以访问静态。

2.4 代码块

代码块就是在程序中编写代码的区域,通常用大括号{}表示代码块。
代码块又分为普通代码块、构造代码块、静态代码块等。

2.4.1 普通代码块

在执行的流程中出现的代码块,称其为普通代码块。
代码(示例):


public class Demo1 {
    public static void main(String[] args) {

        {
            /**
             * 编写在执行顺序的代码流程中的代码块
             * 就是普通代码块
             */
        }
    }
}

2.4.2 构造代码块

在类中的成员代码块,称其为构造代码块,在每次对象创建时执行,执行在构造方法之前。
代码(示例):

class Person1{
    private String name;
    private int age;
    //构造代码块,随着对象的每次创建,执行一次
   //且执行在构造方法之前
    {
        System.out.println("这里是构造代码块");
    }
    //无参构造方法
    public Person1(){
        System.out.println("创建对象时执行");
    }
    //全参构造方法
    public Person1(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

构造代码块区别于构造方法的是:
无论你调用哪一个构造方法来创建对象,构造代码块都必须执行一次。

2.4.3 静态代码块

在类中使用static修饰的成员代码块,称其为静态代码块。
在类加载时,静态代码块执行。
每次程序启动到关闭,只要类被加载了,就始终只执行一次静态代码块。
代码(示例):

public class Demo1 {
    public static void main(String[] args) {

        {
            /**
             * 编写在执行顺序的代码流程中的代码块
             * 就是普通代码块
             */
        }
        Person1 p;
        Person1 p0 = new Person1();
        Person1 p1 = new Person1();
        Person1 p2 = new Person1();
    }
}

class Person1{
    private String name;
    private int age;
    //构造代码块
    {
        System.out.println("这里是构造代码块");
    }
    static {
        System.out.println("静态代码块执行且执行一次");
    }
    //无参构造方法
    public Person1(){
        System.out.println("创建对象时执行");
    }
    //全参构造方法
    public Person1(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

运行结果:

静态代码块执行且执行一次
这里是构造代码块
创建对象时执行
这里是构造代码块
创建对象时执行
这里是构造代码块
创建对象时执行

进程已结束,退出代码 0

重点:
构造方法构造代码块以及静态代码块的执行顺序:

静态代码块 > 构造代码块 > 构造方法

2.5 包

设包是Java语言提供的一种组织代码文件和管理代码文件的技术。

2.5.1 包介绍

  1. 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。

  2. 包就跟文件夹一样,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。这样就可以避免一些类名字冲突的情况。

  3. 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。

2.5.2 包的命名规则

包的命名规则遵循标识符命名规则,同时包名也有自己的命名规范:

  1. 通常由多个单词组成。

  2. 包名单词全部小写

  3. 如果有多个单词,单词之间使用英文( . )分割。

包中java文件的定义:
在.java文件的首部,必须编写类所属哪个包。
格式:

package 包名;

注意:
如果想要跨包访问某个类的成员,
则此类的成员需要使用权限修饰符“public

Java中默认的导入包是“java.lang”包,其中包含了“String”类、“System”类、“Math”类等常用类。

2.5.3 import 关键字

格式:
import 包名.类名;

代码(示例):

package test725;

import java.util.Scanner;

public class outTest {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
	}
}

在上面一段代码中,如果需要从控制台获取用户输入内容时,就需要导入Scanner包。

2.6 权限修饰符

权限修饰符可以访问我们在Java需要的资源,比如类的方法、类的属性、构造方法等。
下面是4种权限修饰符的一些使用范围。

修饰符 子类 其他包
public
protected ×
default × ×
private × × ×

三、面向对象(高级篇)

3 面向对象高级

3.1 继承

继承是java面向对象编程技术的一块基石,因为它允许创建等级层次的类。
继承就是子类继承父类的特征和行为
使得子类对象(实例)具有父类的实例域和方法
或子类从父类继承方法,使得子类具有父类相同的行为

继承格式:

//格式:
class 父类{

}
class 子类 extends 父类{

}

代码(示例):

public class Demo4 {
    public static void main(String[] args) {
        //创建子类的对象
        Student4 s = new Student4();
        s.setName("小明");//调用父类的setName设置姓名
        s.setAge(18);//调用父类的setAge设置年龄
        s.say();//调用父类的方法
    }
}

//格式:
class Person4{
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void say(){
        System.out.println("你好!我是:"+name+", 今年:"+age+"岁。");
    }
}

/**
 * 继承用到 extends关键字
 */
class Student4 extends Person4{
    //子类可以继承父类的特征和行为
}

继承的限制:
Java中只有单继承、多重继承,没有多继承。

3.2 super

super是在子类对象被创建时,先在内存创建父类对象,创建完父类对象后会在子类自动创建super
super就相当于是父类对象的内存地址,子类通过super也就是内存地址去访问父类对象相关资源。
如下图所示:Person作为父类对象,Student作为子类对象,在Student中自动生成super,而super对应父类对象的内存地址0x123,这样子类就可以通过super去访问父类对象的属性name和age了。
什么是Java面向对象(实例详解)_第1张图片

通过super

可以访问父类构造方法属性方法

调用super构造方法的代码,必须写在子类构造方法的第一行。
代码(示例):

class Student4 extends Person4{
    //子类构造方法
    public Student4(){
        //super必须写 在构造方法第一行
        super("张三",18);//访问构造方法
        super.sex = "男";//访问属性(父类的属性不能是private)
        super.say();//访问方法
    }
}

3.3 重写

重写主要是用在子类继承父类的方法时,父类的方法不符合某些要求,需要对该方法进行重写。

重写(override)规则:

  1. 参数列表必须完全与被重写方法的相同;

  2. 返回类型必须完全与被重写方法的返回类型相同;

  3. 访问权限不能比父类中被重写的方法的访问权限更低;

  4. 父类的成员方法只能被它的子类重写;

  5. 声明为staticprivate的方法不能被重写,但是可以再次声明。

代码(示例):

public class Demo5 {
    public static void main(String[] args) {
        Student s = new Student();
        s.say();//输出:西出阳关无故人
    }
}
//父类
class Person{
    public void say(){
        System.out.println("锄禾日当午,汗滴禾下土。");
    }
}
//子类
class Student extends Person{
    //重写父类say()
    public void say(){
        System.out.println("西出阳关无故人");
    }
}

重点:

重写与重载的区别:

区别 重写(Override) 重载(Overload)
发生的位置 字父类中 一个类中
参数列表限制 必须相同 必须不同
返回值类型 必须一致 无关
访问权限 子类不能小于父类 无关
异常处理 范围更小,不能抛出新的异常 无关

3.4 final

final关键字:

1.final用于修饰属性、变量

变量成为了常量,无法对其再次进行赋值

final修饰的局部变量,只能赋值一次(也可先声明后赋值一次)

final修饰的是成员属性,必须在声明时赋值

代码(示例):

final int a = 10;
final int b;//先声明
b = 20;//只能赋值一次

2.final用于修饰类

final修饰的类,不可以被子类继承

代码(示例):

final class Person{
	//被final修饰后,Person不能被继承
}

3.final用于修饰方法

final修饰的方法,不能被子类重写

代码(示例):

class Person{
	//被final修饰的方法,不能被子类声明
    final public void say(){
        System.out.println("锄禾日当午,汗滴禾下土。");
    }
}

3.5 抽象类

3.5.1 概念

抽象类必须使用abstract class声明
一个抽象类中可以没有抽象方法。
抽象方法必须写在抽象类或接口中。

代码格式:

abstract class 类名{
	//抽象类
}

3.5.2 抽象方法

只声明而未实现的方法成为抽象方法
未实现指的是:没有{}方法体。
抽象方法必须使用abstract关键字声明。

代码格式:

abstract class 类名{//抽象类
	//抽象方法,只声明,不实现具体内容
	public abstract void 方法名();

3.5.3 不能被实例化

在抽象类的使用中有几个原则:

  • 抽象类本身是不能直接进行实例化操作的,即:不能只用关键字new完成。

  • 一个抽象类必须被子类所继承,被继承的子类(如果不是抽象类)则必须覆写(重写)抽象类中的全部抽象方法。

3.5.4 常见问题

1、抽象类能否使用final声明?

答:不能。因为final属于修饰的类是不可以被继承,即不饿有子类的,而抽象类必须有子类才有意义,所以不能使用final声明。

2、抽象类能否有构造方法?

答:有构造方法。而且子类对象实例化的流程与普通类的继承是一样的,都是要先调用父类中的构造方法,之后再调用子类自己的构造方法。

3.5.5 抽象类和普通类的区别

1、抽象类必须用publicprotected修饰,如果为private修饰,那么子类则无法继承,也就无法实现其抽象方法。

2、抽象类不可以使用new关键字创建对象,但是在子类创建对象时,抽象父类也会被JVM实例化。

3、如果一个子类继承抽象类,那么必须实现其所有的抽象方法。
如果有未实现的抽象方法,那么子类也必须定义为abstract类。

3.6 接口

3.6.1 概念

如果一个类中的全部方法都是抽象类,全部属性都是全局常量,那么此时就可以将这个类定义成一个接口

定义格式:

interface 接口名称{
	全局常量;
	抽象方法;
}

3.6.2 面向接口编程思想

这种思想是定义(规范、约束)与实现(名实分离的原则)的分离。

优点:

  1. 降低程序的耦合性

  2. 易于程序的扩展

  3. 有利于程序的维护

3.6.3 全局常量和抽象方法的简写

因为接口本身都是由全局常量和抽象方法组成,所以接口中的成员定义可以简写:
1、全局常量编写时,可以省略public static final 关键字
例如:

public static final String NAME = "张三";

简写后:

String NAME = "张三";

2、抽象方法编写时,可以省略public abstract 关键字,
例如:

public abstract void say();

简写后:

void say();

3.6.4 接口的实现

接口的实现跟抽象很类似,但是又不完全同于抽象。
在接口的实现中,需要用到implemnts关键字。
接口可以进行多实现操作:
格式:

class 子类 implements 父接口1,父接口2...{

}

以上的代码成为接口的实现,那么如果一个类即要实现接口,又要继承抽象类的话,则按照以下的格式编写即可:

class 子类 extends 父类 implements 父接口1,父接口2...{

}

3.6.5 接口的继承

接口因为都是抽象抽象部分,不存在具体的实现,所以允许多继承。

例如:

interfacce C extends A,B{

}

注意:
一个接口想要使用,必须依靠子类。
子类(如果不是抽象类的话)要实现接口中的所有抽象方法。

3.6.6 接口和抽象类的区别

1、抽象类要被子类继承,接口要被类实现。
2、接口只能声明抽象方法,抽象类中可以声明抽象方法,也可以写非抽象方法。
3、接口里定义的变量只能是公共的静态常量,抽象类种的变量是普通变量。
4、抽象类使用继承来使用,无法多继承。接口使用实现来使用,可以多实现。
5、抽象类中可以包含static方法,但是接口中不允许。(静态方法不能被子类重写,因此接口中不能声明静态方法)
6、接口不能有构造方法,但是抽象类可以有。

这里写一个简单的代码示例:

接口1(Person):

//接口
public interface Person {
    void say();
}

接口2(Person1):

public interface Person1 {
    String name = "张三";
    int age = 18;
}

子类1(单实现):

public class Docter implements Person {
    @Override
    public void say() {
        System.out.println("我是医生");
    }
}

子类2(多实现):

public class Student4 implements Person,Person1{

    @Override
    public void say() {
        System.out.println("我叫:"+name+",今年"+age);
    }
}

程序入口测试:

public class Demo4 {

    public static void main(String[] args) {
        Student4 s = new Student4();
        say(s);
        
    }
    public static void say(Person p){
        p.say();
    }
}

运行结果:

我叫:张三,今年18
我是医生

Process finished with exit code 0

3.7 多态

多态就是对象的多种表现形式。

3.7.1 多态的体现

对象的多态性,从概念上非常好理解,在类中有子类和父类之分,子类就是父类的一种形态,对象多态性就是这样来的。

注意:
方法的重载和重写也是多态的一种,不过是方法的多态。
重载:一个类中方法的多态性体现。
重写:子父类中方法的多态性体现。

3.7.2 多态的使用

多态的使用也就是对象的类型转换。
格式1:
向上转型:将子类实例变为父类实例

父类 父类对象 = 子类实例;

//代码(示例):
Student s = new Student();
Person p = s;
p.say();

格式2:
向下转型:将父类实例变为子类实例

子类 子类对象 = 父类实例(子类)

//代码(示例):
Student s = new Student();
Person p1 = s;
Student s1 = (Student)p1;
s1.say();

3.7.3 instanceof

instanceof关键字:
作用:判断某个对象是否是指定类的实例,则可以使用instanceof关键字

格式:

实例化对象 instanceof//此操作返回boolean类型的数据

代码(示例):


public class Demo4 {
    public static void main(String[] args) {
        //创建学生类
        Student4 s = new Student4();
        say(s);//打印:我叫:张三,今年18

        //创建医生类
        Docter d = new Docter();
        say(d);//打印:必须传进Student4形态类可以。
        //因为医生类是跟学生类同一级,故不能把医生类强制转换为学生类
        //只能把医生类转化为Person类,而不能转为Student类。
    }
    public static void say(Person p){
        //判断传入的对象是此类的哪种形态
        if(p instanceof Student4){
            Student4 s = (Student4)p;
            s.say();
        }else{
            System.out.println("必须传进Student4形态类可以。");
        }
    }
}

3.8 Object类

3.8.1 概念

Object类是所有类的父类,如果一个类没有明确的继承某一个具体的类,则将默认继承Object类。

例如我们定义一个类:

public class Person{
}

当没有其它继承时,它其实在使用过程中是这样的:

public class Person extends Object{
}

3.8.2 Object的多态

使用Object可以接受任意的引用数据类型

代码(示例):

public class Demo1 {
    public static void main(String[] args) {
        String name = "张三";
        say(name);
        int age = 18;
        say(18);
    }
    //使用Object可以接收任何数据
    public static void say(Object o){
        System.out.println(o);
    }
}

简单的说,Object是Java中的万物,它在java语言中有很多方法、比如toString()、equals()、hashCode()等等。

3.8.3 toString

在程序中,一般建议在所有类中都去重写Object中的toString方法。
此方法的作用:返回对象的字符串表示形式。
ObjecttoString方法,返回对象的内存地址。

代码(示例):

public class Person6 {
    private String name;
    private int age;
    
    //toString方法
    @Override
    //重写后可以根据自己下需求去更改方法
    public String toString() {
        return "Person6{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

小提示:
在IDEA编辑器中使用快捷键Alt+Insert可快速找到toString方法,如下图,
在eclipse中的快捷键是Alt+Shift+s
什么是Java面向对象(实例详解)_第2张图片

3.8.4 equals

建议重写Object中的equals(Object obj)方法。
此方法的作用:指示某个其他对象是否“等于”此对象。

Objectequals方法:
实现了对象上最具区别的可能等价关系;
也就是说,对于任何非空引用值x和y,
当且仅当x和y引用同一对象(x == y具有值true)时,此方法返回true

equals方法重写时的五个特性:

自反性:对于任何非空的参考值x, x.equals(x)应该返回true

对称性:对于任何非空引用值x和y,x.equals(y)应该返回true当且仅当y.equals(x)回报true

传递性:对于任何非空引用值x,y和z,如果x.equals(y)回报true,而y.equals(z)回报true,那么有x.equals(z)同样返回true

一致性:对于任何非空引用值x和y,多次调用x.equals(y)始终返回true或始终返回false,前提是未修改对象上equals上比较中使用的属性。

非空性:对于任何非空的参考值x,x.equals(null)应该返回false

案例:使用equals()完成随机数的比较

3.9 内部类

3.9.1 概念

在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类

广泛意义上的内部类一般来说包括这四种:

  1. 成员内部类
  2. 局部内部类
  3. 匿名内部类
  4. 静态内部类

3.9.2 成员内部类

成员内部类是最普通的内部类,它的定义为:
位于另一个类的内部

代码(示例):

class Outer {
	private String name = "张三";
	private int age = 18;
	public Outer(String s){
		this.s = s;
	}
	//内部类
	class Inner {
		//内部类的写法和外部类没有太大的区别
		private String name = "李四";
		private int age = 20;
		public void say() {			
			System.out.println("我叫:"+name+",今年"+age+"岁");
			System.out.println("我叫:"+Outer.this.name+",今年"+Outer.this.age+"岁");
		}
	}
}

特点:
成员内部类可以无条件访问外部类的所有成员属性和成员方法,包括private成员和静态成员。

注意:
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,也就说在访问同名情况下,调用的是成员内部类的成员。

如果需要访问外部类的同名成员,需要以下面的形式进行访问:

外部类.this.成员变量
外部类.this.成员方法

外部使用成员内部类格式:

Outer o = new Outer();
Outer.Inner i = o.new Inner();

代码(示例):

public class Demo8 {
    public static void main(String[] args) {
        Outer o = new Outer();
        Outer.Inner i = o.new Inner();
        i.say();
    }
}

运行结果:

我叫:李四,今年20岁
我叫:张三,今年18

3.9.3 局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类。

它和成员内部类的区别:
在于局部内部类的访问仅限于方法内或者该作用域内。

class Person7{
    public Person7(){}
}
class Man{
    public Man(){}

    public Person7 getPerson(){
        class Student7 extends Person7{//局部内部类
            String name = "王五";
        }
        return new Student7();
    }
}

注意:
局部内部类就像是方法里面的一个局部变量一样,
是不能有publicprotectedprivate、以及static修饰符的。

3.9.4 匿名内部类

匿名内部类由于没有赋予名字,所有它的创建方式有点奇怪
创建格式:

new 父类构造器(参数列表) |实现接口(){
	//匿名内部类
}

在这里我们看到使用匿名内部类,必须要继承一个父亲类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。

在使用匿名内部类的过程中,我们需要注意以下几点:

  1. 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
  2. 匿名内部类中是不能定义构造函数的。
  3. 匿名内部类中不能存在任何的静态成员变量和静态方法。
  4. 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
  5. 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
  6. 只能访问final型的局部变量。

3.9.5 静态内部类

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。
静态内部类是不需要依赖于外部类对象的,这点和类的静态成员属性有点类似,并且他不能使用外部类的非static成员变量或者方法。

格式:

class Outer{
	public Outer(){
	}
	static class Inner{//静态内部类
		public Inner(){
		}
	}
}

3.10 包装类

3.10.1 概述

在Java中有一个设计的原则“一切皆对象”,那么这样一来Java中的一些基本数据类型,就完全不符合这种设计思想,因为Java中的八种基本数据类型并不是引用数据类型,所以Java中为了解决这样的问题,引入了八种基本数据类型的包装类。

序号 基本数据类型 包装类
1 int Integer
2 char Character
3 float Float
4 double Double
5 boolean Boolean
6 byte Byte
7 short Short
8 long Long

以上的八种包装类,可以将基本数据类型按照类的形式进行操作。
上面八种包装类又分为两种大的类型Number和Object。

  • Number:Integer、Short、Long、Double、Float、Byte都是Number的子类表示是一个数字。
  • Object:Character、Boolean都是Object的直接子类。

3.10.2 装箱和拆箱操作

将一个基本数据类型变为包装类,那么这样的操作称为装箱操作
将一个包装类变为一个基本数据类型,这样的操作称为拆箱操作

总结

以上就是我对面向对象知识整理做一些笔记,或许有些地方写得还没有那么全面,欢迎指出,给出合理性的建议。

这篇算是我第一次做知识方面的笔记,希望在某些知识点上可以帮到大家。

你可能感兴趣的:(#,面向对象,javaSE,java,面向对象编程)