Java基础:面向对象:类、属性和方法

在这里插入图片描述

前言

面向对象是学习 Java 一种非常重要的开发思想,但是面向对象并不是 Java 所特有的思想,这里大家不要搞混了。

下面就让我们一起来探讨面向对象的思想,面向对象的思想已经逐步取代了过程化的思想 — 面向过程,Java 是面向对象的高级编程语言,面向对象语言具有如下特征:

  • 面向对象是一种常见的思想,比较符合人们的思考习惯;
  • 面向对象可以将复杂的业务逻辑简单化,增强代码复用性;
  • 面向对象具有抽象封装继承多态等特性。

面向对象的编程语言主要有:C++JavaC#等。

所以必须熟悉面向对象的思想才能编写出良好的 Java 程序。

1. Java中的类

现在我们来认识一个面向对象的新的概念 — ,什么是类?类是一系列对象的抽象

如何理解类的概念?就比如书籍,书籍它是抽象的,泛指所有类型的所有书,因为书籍一词并没有指向具体的某一本书(具体的某一个对象),类就相当于是书的抽象定义,大多数面向对象的语言都使用 class 来定义类,它告诉你它里面定义的对象都是什么样的,我们一般使用下面来定义类:

class ClassName {
	// body;
}

代码段中涉及一个新的概念 // ,这个我们后面会说。上面,你声明了一个 class 类,现在,你就可以使用关键字 new 来创建一个该类的对象:

ClassName classname = new ClassName();

一般,类的命名遵循驼峰原则,它的定义如下:

骆驼式命名法(Camel-Case)又称驼峰式命名法,是电脑程式编写时的一套命名规则(惯例)。正如它的名称 CamelCase 所表示的那样,是指混合使用大小写字母来构成变量和函数的名字。程序员们为了自己的代码能更容易的在同行之间交流,所以多采取统一的可读性比较好的命名方式。

2. Java中的对象

在 Java 中,万事万物都是对象。这句话相信你一定不陌生,尽管一切都看作是对象,但是你操纵的却是一个对象的 引用(reference)。在这里有一个很形象的比喻:你可以把车钥匙和车看作是一组对象引用和对象的组合。当你想要开车的时候,你首先需要拿出车钥匙点击开锁的选项,停车时,你需要点击加锁来锁车。车钥匙相当于就是引用,车就是对象,由车钥匙来驱动车的加锁和开锁。并且,即使没有车的存在,车钥匙也是一个独立存在的实体,也就是说,你有一个对象引用,但你不一定需要一个对象与之关联,也就是:

Car car;

这里创建的只是引用,而并非对象,但是如果你想要使用car这个引用时,程序会抛出一个异常,因为car这个对象还没有被实例化,只是声明了一个变量而已。

未实例化的变量,就好比某园区大门口需要一个保安值守,这个保安其实就是声明好的的一个变量,但是如果保安还没到岗,那保安这个变量就没有被实例化,那你这个时候喊保安去查一个人的资料,由于保安都没到岗,此时能执行你的命令吗?肯定就是无法达成的任务!因而会抛出异常!

一种安全的做法是,在创建对象引用时,用关键字new创建一个对象并赋给它:

Car car = new Car();

在 Java 中,一旦创建了一个引用,就希望它能与一个新的对象进行关联,通常使用 new 操作符来实现这一目的。new 的意思是,给我创建一个新对象

3. 类的属性和方法

一个类最基本的要素就是它的属性方法

3.1 类的属性

属性也被称为字段,它是类的重要组成部分,属性可以是任意类型的对象,也可以是基本数据类型。例如下:

class A{
  int a;
  Apple apple;
}

类中还应该包括方法,方法表示的是 做某些事情的方式。方法其实就是函数,只不过 Java 习惯把函数称为方法。这种叫法也体现了面向对象的概念。

3.2 类的方法

方法的基本组成包括 方法名称、参数、返回值和方法体, 下面是它的示例:

public int getResult(){
  // ...
  return 1;
}

其中,getResult 就是方法名称、() 里面表示方法接收的参数、return 表示方法的返回值。有一种特殊的参数类型 — void 表示方法无返回值。{} 包含的代码段被称为方法体。

3.2.1 构造方法

在 Java 中,有一种特殊的方法被称为 构造方法,也被称为构造函数、构造器等。在 Java 中,通过提供这个构造器,来确保每个对象都被初始化。构造方法只能在对象的创建时期调用一次,保证了对象初始化的进行。构造方法比较特殊,它没有参数类型和返回值,它的名称要和类名保持一致,并且构造方法可以有多个,下面是一个构造方法的示例:

class Apple {
  
  int sum;
  String color;
  
  public Apple(){}
  public Apple(int sum){}
  public Apple(String color){}
  public Apple(int sum,String color){}
  
}

上面定义了一个 Apple 类,你会发现这个 Apple 类没有参数类型和返回值,并且有多个以 Apple 同名的方法,而且各个 Apple 的参数列表都不一样,这其实是一种多态的体现,我们后面会说。在定义完成构造方法后,我们就能够创建 Apple 对象了。

class createApple {

    public static void main(String[] args) {
        Apple apple1 = new Apple();
        Apple apple2 = new Apple(1);
        Apple apple3 = new Apple("red");
        Apple apple4 = new Apple(2,"color");

    }
}

如上面所示,我们定义了四个 Apple 对象,并调用了 Apple 的四种不同的构造方法,其中,不加任何参数的构造方法被称为默认的构造方法,也就是:

Apple apple1 = new Apple();

如果类中没有定义任何构造方法,那么 JVM 会为你自动生成一个构造方法,如下:

class Apple {
  
  int sum;
  String color;
}

class createApple {

    public static void main(String[] args) {
        Apple apple1 = new Apple();

    }
}

上面代码不会发生编译错误,因为 Apple 对象包含了一个默认的构造方法。

默认的构造方法也被称为默认构造器或者无参构造器

这里需要注意一点的是,即使 JVM 会为你默认添加一个无参的构造器,但是如果你手动定义了任何一个构造方法,JVM 就不再为你提供默认的构造器,你必须手动指定,否则会出现编译错误
显示的错误是,必须提供 Apple 带有 int 参数的构造函数,而默认的无参构造函数没有被允许使用。

3.2.2 方法重载

在 Java 中一个很重要的概念是方法的重载,它是类名的不同表现形式。我们上面说到了构造函数,其实构造函数也是重载的一种。另外一种就是方法的重载

public class Apple {

    int sum;
    String color;

    public Apple(){}
    public Apple(int sum){}
    
    public int getApple(int num){
        return 1;
    }
    
    public String getApple(String color){
        return "color";
    }

}

如上面所示,就有两种重载的方式,一种是 Apple 构造函数的重载,一种是 getApple 方法的重载。

但是这样就涉及到一个问题,要是有几个相同的名字,Java 如何知道你调用的是哪个方法呢?这里记住一点即可,每个重载的方法都有独一无二的参数列表。其中包括参数的类型顺序参数数量等,满足一种一个因素就构成了重载的必要条件。

请记住下面重载的条件:

  1. 方法名称必须相同
  2. 参数列表必须不同(个数不同、或类型不同、参数类型排列顺序不同等)。
  3. 方法的返回类型可以相同也可以不相同
  4. 仅仅返回类型不同不足以成为方法的重载。
  5. 重载是发生在编译时的,因为编译器可以根据参数的类型来选择使用哪个方法。
3.2.3 方法重写

方法的重写与重载虽然名字很相似,但却完全是不同的东西。方法重写的描述是对子类和父类之间的。而重载指的是同一类中的。例如如下代码

class Fruit {
 
	public void eat(){
    System.out.printl('eat fruit');
  }
}

class Apple extends Fruit{
  
  @Override
  public void eat(){
    System.out.printl('eat apple');
  }
}

上面这段代码描述的就是方法重写的代码,你可以看到,子类 Apple 中的方法和父类 Fruit 中的方法同名,所以,我们能够推断出重写的原则:

  1. 重写的方法必须要和父类保持一致,包括返回值类型、方法名、参数列表 也都必须完全一样。
  2. 重写的方法可使用 @Override 注解(可选)来标识。
  3. 子类中重写方法的访问权限不能低于父类中方法的访问权限。
3.2.4 重载和重写区别

在这里插入图片描述在Java中,重载(Overload)和重写(Override)是两个重要的概念,它们都是实现多态性的方式,但它们之间存在明显的区别。

重载(Overload)‌

  1. 重载发生在同一个类中,允许存在多个同名方法,只要它们的参数列表(即参数的类型、数量和顺序)不同即可。
  2. 重载基于编译时的多态性,即在编译时确定调用哪个方法。
  3. 重载的方法可以有不同的返回类型,但返回类型不是区分重载方法的唯一标准。
  4. 重载的方法可以抛出不同的异常,也可以有不同的访问修饰符。

重写(Override)‌

  1. 重写发生在子类与父类之间,当子类需要改变父类的已有方法时,可以在子类中重新定义该方法。
  2. 重写基于运行时的多态性,即在运行时确定调用哪个方法。
  3. 重写的方法必须和父类中被重写的方法有相同的方法名、参数列表和返回类型。
  4. 重写的方法不能抛出新的检查型异常或者比被重写方法声明更加宽泛的检查型异常。
  5. 重写方法的访问修饰符的限制一定要大于等于被重写方法的访问修饰符。

4. 初始化

在这里插入图片描述

4.1 类的初始化

上面我们创建出来了一个 Car 这个对象,在使用 new 关键字创建一个Car对象的时候,其实是调用了这个对象无参数的构造方法进行的初始化,也就是如下这段代码

class Car{
  public Car(){}
}

这个无参数的构造函数是Java程序默认自带的,可以隐藏不写,由 JVM 自动添加。也就是说,构造函数能够确保类的初始化。

4.2 成员初始化

Java 会尽量保证每个变量在使用前都会获得初始化,初始化涉及两种初始化。

  1. 一种是编译器默认指定的字段初始化,基本数据类型的初始化。
  2. 一种是其他对象类型的初始化,String 也是一种对象,对象的初始值都为 null ,其中也包括基本类型的包装类

指定数值的初始化,例如:

int a = 11

也就是说, 指定 a 的初始化值不是 0 ,而是 11。其他基本类型和对象类型也是一样的。

4.3 构造器初始化

可以利用构造器来对某些属性进行初始化,确定初始值,例如

public class Counter{
  int i;
  public Counter(){
    i = 11;
  }
}

利用构造函数,能够把 i 的值初始化为 11。

4.4 初始化顺序

在这里插入图片描述
首先先来看一下有哪些需要探讨的初始化顺序:

  1. 静态属性:static 开头定义的属性
  2. 静态方法块: static {} 包起来的代码块
  3. 普通属性: 非 static 定义的属性
  4. 普通方法块: {} 包起来的代码块
  5. 构造函数: 类名相同的方法
  6. 方法: 普通方法
public class LifeCycle {
    // 静态属性
    private static String staticField = getStaticField();
    
    // 静态方法块
    static {
        System.out.println(staticField);
        System.out.println("静态方法块初始化");
    }
    
    // 普通属性
    private String field = getField();
    
    // 普通方法块
    {
        System.out.println(field);
    }
    
    // 构造函数
    public LifeCycle() {
        System.out.println("构造函数初始化");
    }

    public static String getStaticField() {
        String statiFiled = "Static Field Initial";
        return statiFiled;
    }

    public static String getField() {
        String field = "Field Initial";
        return field ;
    }   
    
    // 入库函数
    public static void main(String[] args) {
        new LifeCycle();
    }
}

这段代码的执行结果就反应了它的初始化顺序,依次为:
静态属性初始化、静态方法块初始化、普通属性初始化、普通方法块初始化、构造函数初始化。

4.5 数组初始化

数组是相同类型的、用一个标识符名称封装到一起的一个对象序列基本类型数据序列。数组是通过方括号下标操作符 [] 来定义使用。

一般数组是这么定义的:

int[] a1;

//或者

int a1[];

上面这两种数组变量声明的格式的,其效果是一样的。

  1. 直接给每个元素赋值 :
int array[4] = {1,2,3,4};
  1. 给一部分赋值,后面的都为 0:
int array[4] = {1,2};
  1. 由赋值参数个数决定数组的个数:
int array[] = {1,2};

可变参数列表
Java 中一种数组冷门的用法就是可变参数 ,可变参数的定义如下

public int add(int... numbers){
  int sum = 0;
  for(int num : numbers){
    sum += num;
  }
  return sum;
}

然后,你可以使用下面这几种方式进行可变参数的调用

add();  // 不传参数
add(1);  // 传递一个参数
add(2,1);  // 传递多个参数
add(new int[] {1, 3, 2});  // 传递数组

5. 对象的销毁

虽然 Java 语言是基于 C++ 的,但是它和 C/C++ 一个重要的特征就是Java不需要手动管理对象的销毁工作。它是由 Java 虚拟机(JVM)进行管理和销毁的。虽然我们不需要手动管理对象,但是你需要知道 对象作用域 这个概念。

5.1 对象作用域

许多数语言都有作用域(scope) 这个概念。作用域决定了其内部定义的变量名的可见性和生命周期。在 C、C++ 和 Java 中,作用域通常由 {} 的位置来决定,这也是我们常说的代码块。例如:

{  
  int a = 11;  
  {    
    int b = 12;  
  }
}

a 变量会在两个 {} 作用域内有效,而 b 变量的值只能在它自己的 {} 内有效。

虽然存在作用域,但是不允许这样写

{  
  int x = 11;  
  {    
    int x = 12;  
  }
}

这种写法在 C/C++ 中是可以的,但是在 Java 中不允许这样写,因为 Java 设计者认为这样写会导致程序混乱,在Java中,同一个变量在同一个作用域范围内只能定义一次。

6. this 和 super

thissuper 都是 Java 中的关键字。
this 表示的当前对象,this 可以调用方法、调用属性和指向对象本身。this 在 Java 中的使用一般有三种:指向当前对象

public class Apple {

    int i = 0;

    Apple eatApple(){
        i++;
        return this;
    }

    public static void main(String[] args) {
        Apple apple = new Apple();
        apple.eatApple().eatApple();
    }
}

这段代码比较精妙,精妙在哪呢?我一个 eatApple() 方法竟然可以调用多次,你在后面还可以继续调用,这就很神奇了,为啥呢?其实就是 this 在作祟了,我在 eatApple 方法中加了一个 return this 的返回值,也就是说哪个对象调用 eatApple 方法都能返回对象的自身。

this 还可以修饰属性,最常见的就是在构造方法中使用 this ,如下所示

public class Apple {

    private int num;
    
    public Apple(int num){
        this.num = num;
    }

    public static void main(String[] args) {
        new Apple(10);
    }
}

main 方法中传递了一个 int 值为 10 的参数,它表示的就是苹果的数量,并把这个数量赋给了 num 全局变量。所以 num 的值现在就是 10

this 还可以和构造函数一起使用,充当一个全局关键字的效果

public class Apple {

    private int num;
    private String color;

    public Apple(int num){
        this(num,"红色");
    }
    
    public Apple(String color){
        this(1,color);
    }

    public Apple(int num, String color) {
        this.num = num;
        this.color = color;
    }
}

你会发现上面这段代码使用的不是 this, 而是 this(参数)。它相当于调用了其他构造方法,然后传递参数进去。这里注意一点:this() 必须放在构造方法的第一行,否则编译不通过。

如果你把 this 理解为指向自身的一个引用,那么 super 就是指向父类的一个引用。super 关键字和 this 一样,你可以使用 super.对象 来引用父类的成员,如下:

public class Fruit {

    int num;
    String color;

    public void eat(){
        System.out.println("eat Fruit");
    }
}

public class Apple extends Fruit{

    @Override
    public void eat() {
        super.num = 10;
        System.out.println("eat " + num + " Apple");
    }

}

你也可以使用 super(参数) 来调用父类的构造函数,这里不再举例子了。
下面为你汇总了 this 关键字和 super 关键字的比较。
在这里插入图片描述

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码踏云端

你的打赏是我精心创作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值