第06章 面向对象编程(基础)

news2024/11/20 3:29:48

在这里插入图片描述

一 面向对象编程概述

1.1 程序设计的思路

面向对象,是软件开发中的一类编程风格、开发范式。除了面向对象,还有面向过程指令式编程函数式编程。在所有的编程范式中,我们接触最多的还是面向过程和面向对象两种。

类比:史书类型

  • 纪传体:以人物传记为中心,“本纪”叙述帝王,“世家”记叙王侯封国和特殊人物,“列传”记叙民间人物。
  • 编年体:按年、月、日顺序编写。
  • 国别体:是一部分国记事的历史散文,分载多国历史。

早期先有面向过程思想,随着软件规模的扩大,问题复杂性的提高,面向过程的弊端越来越明显,出现了面向对象思想并成为目前主流的方式。
1. 面向过程的程序设计思想(Process-Oriented Programming),简称POP

  • 关注的焦点是过程:过程就是操作数据的步骤。如果某个过程的实现代码重复出现,那么就可以把这个过程抽取为一个函数。这样就可以大大简化冗余代码,便于维护。
  • 典型的语言:C语言
  • 代码结构:以函数为组织单位。
  • 是一种“执行者思维”,适合解决简单问题。扩展能力差、后期维护难度较大。

2. 面向对象的程序设计思想( Object Oriented Programming),简称OOP

  • 关注的焦点是:在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,用类来表示。
  • 典型的语言:Java、C#、C++、Python、Ruby和PHP等
  • 代码结构:以为组织单位。每种事物都具备自己的属性行为/功能
  • 是一种“设计者思维”,适合解决复杂问题。代码扩展性强、可维护性高。

1.2 由实际问题考虑如何设计程序

思考1:如何开车?

面向过程思想思考问题时,我们首先思考“怎么按步骤实现?”并将步骤对应成方法,一步一步,最终完成。 这个适合简单任务,不需要过多协作的情况。针对如何开车,可以列出步骤:
在这里插入图片描述
面向过程适合简单、不需要协作的事务,重点关注如何执行。
思考2:如何造车?

造车太复杂,需要很多协作才能完成。此时我们思考的是“车怎么设计?”,而不是“怎么按特定步骤造车的问题”。这就是思维方式的转变,前者就是面向对象思想。所以,面向对象(Oriented-Object)思想更契合人的思维模式。

用面向对象思想思考“如何设计车”:
在这里插入图片描述
自然地,我们就会从“车由什么组成”开始思考。发现,车由如下结构组成:
在这里插入图片描述
我们找轮胎厂完成制造轮胎的步骤,发动机厂完成制造发动机的步骤,…;这样,大家可以同时进行车的制造,最终进行组装,大大提高了效率。但是,具体到轮胎厂的一个流水线操作,仍然是有步骤的,还是离不开面向过程思维!

因此,面向对象可以帮助我们从宏观上把握、从整体上分析整个系统。 但是,具体到实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去处理。

注意:
我们千万不要把面向过程和面向对象对立起来。他们是相辅相成的。面向对象离不开面向过程!
当需求单一,或者简单时,我们一步步去操作没问题,并且效率也挺高。
可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦了,这时就开始思索,**能不能把这些步骤和功能进行封装,封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。**这样结构就清晰了很多。用的时候,找到对应的类就可以了。这就是面向对象的思想。

类比举例2:人把大象装进冰箱

  • 面向过程
1.打开冰箱

2.把大象装进冰箱

3.把冰箱门关住
  • 面向对象
{
    打开(冰箱){
		冰箱.开门();	
    }
    操作(大象){
             大象.进入(冰箱);
    }
    关闭(冰箱){   
          冰箱.关门();     
    }
}

冰箱{
     开门(){ }  
     关门(){ }
}

大象{
     进入(冰箱){  }
}

二 Java语言的基本元素:类和对象

2.1 类和对象概述

类(Class)对象(Object)是面向对象的核心概念。

1、什么是类

:具有相同特征的事物的抽象描述,是抽象的、概念上的定义。

2、什么是对象

对象:实际存在的该类事物的每个个体,是具体的,因而也称为实例(instance)

2.2 类的成员概述

面向对象程序设计的重点是类的设计

类的设计,其实就是类的成员的设计

  • 现实世界的生物体,大到鲸鱼,小到蚂蚁,都是由最基本的细胞构成的。同理,Java代码世界是由诸多个不同功能的构成的。
    现实生物世界中的细胞又是由什么构成的呢?细胞核、细胞质、…

Java中用类class来描述事物也是如此。类,是一组相关属性行为的集合,这也是类最基本的两个成员。

  • 属性:该类事物的状态信息。对应类中的成员变量
    • 成员变量 <=> 属性 <=> Field
  • 行为:该类事物要做什么操作,或者基于事物的状态能做什么。对应类中的成员方法
    • (成员)方法 <=> 函数 <=> Method
      在这里插入图片描述

2.3 面向对象完成功能的三步骤

步骤1:类的定义

类的定义使用关键字:class。格式如下:

[修饰符] class 类名{
	属性声明;
    方法声明;
}

举例1:

public class Person{
    //声明属性age
    int age ;	                   
    
    //声明方法showAge()
    public void eat() {        
	    System.out.println("人吃饭");
    }
}

举例2:

public class Dog{
    //声明属性
	String type; //种类
	String nickName; //昵称
	String hostName; //主人名称
	
    //声明方法
	public void eat(){ //吃东西
		System.out.println("狗狗进食");		
	}
}
public class Person{
    String name;
    char gender;
    Dog dog;
    
    //喂宠物
    public void feed(){
        dog.eat();
    }
}

步骤2:对象的创建
在这里插入图片描述

  • 创建对象,使用关键字:new
  • 创建对象语法:
//方式1:给创建的对象命名
//把创建的对象用一个引用数据类型的变量保存起来,这样就可以反复使用这个对象了
类名 对象名 = new 类名();

//方式2:
new 类名()//也称为匿名对象

举例:

class PersonTest{
	public static void main(String[] args){
		//创建Person类的对象
		Person per = new Person();
		//创建Dog类的对象
		Dog dog = new Dog();
	}
}

步骤3:对象调用属性或方法

  • 对象是类的一个实例,必然具备该类事物的属性和行为(即方法)。

  • 使用"对象名.属性" 或 "对象名.方法"的方式访问对象成员(包括属性和方法)

举例1:

//声明Animal类
public class Animal { //动物类
    public int legs;

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

    public void move() {
        System.out.println("Move.");
    }
}
//声明测试类
public class AnimalTest {
    public static void main(String args[]) {
        //创建对象
        Animal xb = new Animal();
        xb.legs = 4;//访问属性
        System.out.println(xb.legs);
        xb.eat();//访问方法
        xb.move();//访问方法
    }
}

图示理解:
在这里插入图片描述
举例2:针对前面步骤1的举例2:类的实例化(创建类的对象)

public class Game{
    public static void main(String[] args){
        Person p = new Person();
        //通过Person对象调用属性
        p.name = "zyy";
        p.gender = '男';
        p.dog = new Dog(); //给Person对象的dog属性赋值
        
        //给Person对象的dog属性的type、nickname属性赋值
        p.dog.type = "柯基犬";
        p.dog.nickName = "小白";
        
        //通过Person对象调用方法
        p.feed();
    }
}

2.4 匿名对象 (anonymous object)

  • 我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。

    • 如:new Person().shout();
  • 使用情况

    • 如果一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
    • 我们经常将匿名对象作为实参传递给一个方法调用。

三 对象的内存解析

3.1 JVM内存结构划分

HotSpot Java虚拟机的架构图如下。其中我们主要关心的是运行时数据区部分(Runtime Data Area)。
在这里插入图片描述
其中:
堆(Heap):此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
栈(Stack):是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址)。 方法执行完,自动释放。
方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

3.2 对象内存解析

**
 * @ClassName Person
 * @Author Administrator
 * @Date 2023/11/11 11:26
 * @Version 1.0
 * @Description TODO
 **/
public class Person {
    String name;
    int age;
    boolean isMale;
}
/**
 * @ClassName PersonTest
 * @Author Administrator
 * @Date 2023/11/11 11:26
 * @Version 1.0
 * @Description TODO
 **/
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name="赵同学";
        p1.age = 20;
        p1.isMale = true;

        Person p2 = new Person();
        p2.age = 10;

        Person p3 =new Person();
        p3.name = "郭同学";
    }
}

内存解析图:
在这里插入图片描述

说明:

  • 堆:凡是new出来的结构(对象、数组)都放在堆空间中。
  • 对象的属性存放在堆空间中。
    创建一个类的多个对象(比如p1、p2),则每个对象都拥有当前类的一套"副本"(即属性)。当通
    -过一个对象修改其属性时,不会影响其它对象此属性的值。
  • 当声明一个新的变量使用现有的对象进行赋值时(比如p3 = p1),此时并没有在堆空间中创建新的对象。而是两个变量共同指向了堆空间中同一个对象。当通过一个对象修改属性时,会影响另外一个对象对此属性的调用。

对象名中存储的是什么呢? – 对象地址

package com.tedu.java.oop;

/**
 * @ClassName StudentTest
 * @Author Administrator
 * @Date 2023/11/11 14:45
 * @Version 1.0
 * @Description TODO
 **/
public class StudentTest {
    public static void main(String[] args) {
        // com.tedu.java.oop.Person@1b6d3586
        System.out.println(new Person());

        Person person = new Person();
        // com.tedu.java.oop.Person@4554617c
        System.out.println(person);

        int [] arr = new int[5];
        // [I@74a14482
        System.out.println(arr);
    }
}

直接打印对象名和数组名都是显示“类型@对象的hashCode值",所以说类、数组都是引用数据类型,引用数据类型的变量中存储的是对象的地址,或者说指向堆中对象的首地址。

四 类的成员之一:成员变量(field)

4.1 如何声明成员变量

  • 语法格式:
[修饰符1] class 类名{
    [修饰符2] 数据类型 成员变量名 [= 初始化值]; 
}

说明:

  • 位置要求:必须在类中,方法外
  • 修饰符2(暂不考虑)
    • 常用的权限修饰符有:private、缺省、protected、public。
    • 其他修饰符:static、final 。
  • 数据类型
    • 任何基本数据类型(如int、Boolean) 或 任何引用数据类型。
  • 成员变量名
    • 属于标识符,符合命名规则和规范即可。
  • 初始化值
    • 根据情况,可以显式赋值;也可以不赋值,使用默认值。
      示例:
public class Person{
	private int age;             //声明private变量 age
	public String name =Lila;    //声明public变量 name
}

4.2 成员变量 vs 局部变量

4.2.1 变量的分类:成员变量与局部变量

  • 在方法体外,类体内声明的变量称为成员变量。
  • 在方法体内部等位置声明的变量称为局部变量。
    在这里插入图片描述
    在这里插入图片描述

其中,static可以将成员变量分为两大类,静态变量和非静态变量。其中静态变量又称为类变量,非静态变量又称为实例变量或者属性。接下来先学习实例变量。

4.2.2 成员变量 与 局部变量 的对比

  • 相同点
    • 变量声明的格式相同: 数据类型 变量名 = 初始化值。
    • 变量必须先声明、后初始化、再使用。
    • 变量都有其对应的作用域。只在其作用域内是有效的。
  • 不同点
    1、声明位置和方式
    (1)实例变量:在类中方法外。
    (2)局部变量:在方法体{}中或方法的形参列表、代码块中。
    2、在内存中存储的位置不同
    (1)实例变量:堆。
    (2)局部变量:栈。
    3、生命周期
    (1)实例变量:和对象的生命周期一样,随着对象的创建而存在,随着对象被GC回收而消亡,
    而且每一个对象的实例变量是独立的。
    (2)局部变量:和方法调用的生命周期一样,每一次方法被调用而在存在,随着方法执行的结束而消亡,而且每一次方法调用都是独立。
    4、作用域
    (1)实例变量:通过对象就可以使用,本类中直接调用,其他类中“对象.实例变量”。
    (2)局部变量:出了作用域就不能使用。
    5、修饰符(后面来讲)
    (1)实例变量:public,protected,private,final,volatile,transient等。
    (2)局部变量:final。
    6、默认值
    (1)实例变量:有默认值。
    (2)局部变量:没有,必须手动初始化。其中的形参比较特殊,靠实参给它初始化。

4.2.3 对象属性的默认初始化赋值

当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。
在这里插入图片描述

4.2.4 举例

class Person {
    // 属性
    String name; // 姓名
    int age; // 年龄
    boolean isMale; // 是否是男性

    public void show(String nation){
        // nation 局部变量
        String color; // 局部变量
        color = "yellow";
    }

    // 测试类
    static class PersonTest{
        public static void main(String[] args) {
            Person p = new Person();
            p.show("CHN");
        }
    }
}

在这里插入图片描述

五 类的成员之二:方法(method)

5.1 方法的引入

在这里插入图片描述
《街霸》游戏中,每次人物出拳、出脚或跳跃等动作都需要编写50-80行的代码,在每次出拳、出脚或跳跃的地方都需要重复地编写这50-80行代码,这样程序会变得很臃肿,可读性也非常差。为了解决代码重复编写的问题,可以将出拳、出脚或跳跃的代码提取出来放在一个{}中,并为这段代码起个名字,这样在每次的出拳、出脚或跳跃的地方通过这个名字来调用这个{}的代码就可以了。

上述过程中,所提取出来的代码可以被看作是程序中定义的一个方法,程序在需要出拳、出脚或跳跃时调用该方法即可。

5.2 方法(method、函数)的理解

  • 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数过程
  • 将功能封装为方法的目的是,可以实现代码重用,减少冗余,简化代码
  • Java里的方法不能独立存在,所有的方法必须定义在类里。

5.3 如何声明方法

5.3.1 声明方法的语法格式

[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表]{
        方法体的功能代码
}

(1)一个完整的方法 = 方法头 + 方法体。

  • 方法头就是[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表],也称为方法签名。通常调用方法时只需要关注方法头就可以,从方法头可以看出这个方法的功能和调用格式。
  • 方法体就是方法被调用后要执行的代码。对于调用者来说,不了解方法体如何实现的,并不影响方法的使用。

(2)方法头可能包含5个部分

  • 修饰符:可选的。方法的修饰符也有很多,例如:public、protected、private、static、abstract、native、final、synchronized等,后面会一一学习。

    • 其中,权限修饰符有public、protected、private。在讲封装性之前,我们先默认使用pulbic修饰方法。
    • 其中,根据是否有static,可以将方法分为静态方法和非静态方法。其中静态方法又称为类方法,非静态方法又称为实例方法。
  • 返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者。

    • 无返回值,则声明:void
    • 有返回值,则声明出返回值类型(可以是任意类型)。与方法体中“return 返回值”搭配使用
  • 方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意”

  • 形参列表:表示完成方法体功能时需要外部提供的数据列表。可以包含零个,一个或多个参数。

    • 无论是否有参数,()不能省略
    • 如果有参数,每一个参数都要指定数据类型和参数名,多个参数之间使用逗号分隔,例如:
      • 一个参数: (数据类型 参数名)
      • 二个参数: (数据类型1 参数1, 数据类型2 参数2)
    • 参数的类型可以是基本数据类型、引用数据类型
  • ** throws 异常列表**
    (3)方法体:方法体必须有{}括起来,在{}中编写完成方法功能的代码

(4)关于方法体中return语句的说明:

  • return语句的作用是结束方法的执行,并将方法的结果返回去

  • 如果返回值类型不是void,方法体中必须保证一定有 return 返回值; 语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。

  • 如果返回值类型为void时,方法体中可以没有return语句,如果要用return语句提前结束方法的执行,那么return后面不能跟返回值,直接写return ; 就可以。

  • return语句后面就不能再写其他代码了,否则会报错:Unreachable code

补充:方法的分类:按照是否有形参及返回值
在这里插入图片描述

5.3.2 代码示例

/**
 * @ClassName MethodDefineDemo
 * @Author Administrator
 * @Date 2023/11/14 9:35
 * @Version 1.0
 * @Description TODO
 **/
public class MethodDefineDemo {
    /***
     * @date: 2023/11/14 9:36
     * @param:  * @param
     * @return: void
     * @description: 无参无返回的方法演示
     */
    public void syaHello(){
        System.out.println("hello");
    }
    /***
     * @date: 2023/11/14 9:39
     * @param length 第一个参数,标识矩形的长
     * @param width 第二个参数,表示举行的宽
     * @param sign 第三个参数,标识填充举行图形的符号
     * @return: void
     * @description:
     */
    public void printRectangle(int length,int width,char sign){
        for(int i = 1;i<length;i++){
            for (int j = 1;j<=width;j++){
                System.out.println(sign);
            }
            System.out.println();
        }
    }
    /**
     * 无参有返回值方法的演示
     * @return
     */
    public int getIntBetweenOneToHundred(){
        return (int)(Math.random()*100+1);
    }
    /*** 
     * @date: 2023/11/14 16:17
     * @param a 第一个参数,要比较大小的整数之一
     * @param b 第二个参数,要比较大小的整数之二
     * @return: int 比较大小的两个整数中较大者的值
     * @description: 有参有返回值方法的演示
     */ 
    public int max(int a,int b){
        return a > b ? a : b;
    }
}

5.4 如何调用实例方法

方法通过方法名被调用,且只有被调用才会执行。

5.4.1 方法调用语法格式

对象.方法名([实参列表])

5.4.2 示例

/**
 * @ClassName MethodInvokeDemo
 * @Author Administrator
 * @Date 2023/11/14 16:24
 * @Version 1.0
 * @Description 方法调用案例
 **/
public class MethodInvokeDemo {
    public static void main(String[] args) {
        // 创建对象
        MethodDefineDemo md = new MethodDefineDemo();
        System.out.println("********方法调用演示********");

        // 调用MethodDefineDemo类中无参无返回值的方法sayHello
        md.syaHello();
        md.syaHello();
        md.syaHello();
        // 调用一次,执行一次,不调用不执行
        System.out.println("------------------------------------------------");
        //调用MethodDefineDemo类中有参无返回值的方法printRectangle
        md.printRectangle(5,10,'@');

        System.out.println("------------------------------------------------");
        //调用MethodDefineDemo类中无参有返回值的方法getIntBetweenOneToHundred
        md.getIntBetweenOneToHundred();//语法没问题,就是结果丢失

        int num = md.getIntBetweenOneToHundred();
        System.out.println("num = " + num);

        System.out.println(md.getIntBetweenOneToHundred());
        //上面的代码调用了getIntBetweenOneToHundred三次,这个方法执行了三次

        System.out.println("------------------------------------------------");
        //调用MethodDefineDemo类中有参有返回值的方法max
        md.max(3,6);//语法没问题,就是结果丢失

        int bigger = md.max(5,6);
        System.out.println("bigger = " + bigger);

        System.out.println("8,3中较大者是:" + md.max(8,9));
    }
}

举例2:

//1、创建Scanner的对象
Scanner input = new Scanner(System.in);//System.in默认代表键盘输入

//2、提示输入xx
System.out.print("请输入一个整数:"); //对象.非静态方法(实参列表)

//3、接收输入内容
int num = input.nextInt();  //对象.非静态方法()

5.5 使用的注意点

(1)必须先声明后使用,且方法必须定义在类的内部
(2)调用一次就执行一次,不调用不执行。
(3)方法中可以调用类中的方法或属性,不可以在方法内部定义方法。

正确示例:

{
    方法1(){
        
    }
    方法2(){
        
    }
}

错误示例:

{
    方法1(){
        方法2(){  //位置错误
   		}
    }
}

5.6 关键字return的使用

  • return在方法中的作用:
    • 作用1:结束一个方法。
    • 作用2:结束一个方法的同时,可以返回数据给方法的调用者 。
  • 注意点:在return关键字的直接后面不能声明执行语句。

5.7 方法调用内存分析

  • 方法没有被调用的时候,都在方法区中的字节码文件(.class)中存储。
  • 方法被调用的时候,需要进入到栈内存中运行。方法每调用一次就会在栈中有一个入栈动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值。
  • 当方法执行结束后,会释放该内存,称为出栈,如果方法有返回值,就会把结果返回调用处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。
  • 栈结构:先进后出,后进先出。
    举例分析:
/**
 * @author 尚硅谷-宋红康
 * @create 9:21
 */
public class Person {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.eat();

    }
    public static void eat() {
        sleep();
        System.out.println("人:吃饭");
    }
    public static void sleep(){
        System.out.println("人:睡觉");
        doSport();
    }
    public static void doSport(){
        System.out.println("人:运动");
    }
}

内存分析:
在这里插入图片描述

六 对象数组

数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用类型中的类时,我们称为对象数组。

6.1 案例

定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。 创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
问题一:打印出3年级(state值为3)的学生信息。
问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息。
提示:

  1. 生成随机数:Math.random(),返回值类型double;
  2. 四舍五入取整:Math.round(double d),返回值类型long。
/*
 * 定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。
 */
public class Student {
	
	int number;//学号
	int state;//年级
	int score;//成绩
	
	
	public void info(){
		System.out.println("number : " + number 
				+ ",state : " + state + ",score : " + score);
	}
}
public class StudentTest {

	public static void main(String[] args) {

		// Student s1 = new Student();
		// s1.number = 1;
		// s1.state = (int)(Math.random() * 6 + 1);//[1,6]
		// s1.score = (int)(Math.random() * 101);//[0,100]
		//
		// Student s2 = new Student();
		// s2.number = 2;
		// s2.state = (int)(Math.random() * 6 + 1);//[1,6]
		// s2.score = (int)(Math.random() * 101);//[0,100]
		//
		// //....
		// 对象数组
		// String[] arr = new String[10];
		// 数组的创建
		Student[] students = new Student[20];
		// 通过循环结构给数组的属性赋值
		for (int i = 0; i < students.length; i++) {
			// 数组元素的赋值
			students[i] = new Student();
			// 数组元素是一个对象,给对象的各个属性赋值
			students[i].number = (i + 1);
			students[i].state = (int) (Math.random() * 6 + 1);// [1,6]
			students[i].score = (int) (Math.random() * 101);// [0,100]
		}

		// 问题一:打印出3年级(state值为3)的学生信息。
		for (int i = 0; i < students.length; i++) {

			if (students[i].state == 3) {
//				System.out.println(
//						"number:" + students[i].number + ",state:" + students[i].state + ",score:" + students[i].score);
				students[i].info();
			}
		}
		System.out.println("******************************");
		// 问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
		// 排序前
		for (int i = 0; i < students.length; i++) {
//			System.out.println(
//					"number:" + students[i].number + ",state:" + 
//							students[i].state + ",score:" + students[i].score);	
			students[i].info();
		}
		System.out.println();
		// 排序:
		for (int i = 0; i < students.length - 1; i++) {
			for (int j = 0; j < students.length - 1 - i; j++) {
				if (students[j].score > students[j + 1].score) {
					Student temp = students[j];
					students[j] = students[j + 1];
					students[j + 1] = temp;
				}
			}
		}
		// 排序后:
		for (int i = 0; i < students.length; i++) {
//			System.out.println(
//					"number:" + students[i].number + ",state:" + 
//							students[i].state + ",score:" + students[i].score);			
			students[i].info();
		}
	}
}

内存解析:
在这里插入图片描述

6.2 注意点

对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException

七 再谈方法

7.1 方法的重载(overload)

7.1.1 概念及特点

  • 方法重载:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。
    • 参数列表不同,意味着参数个数或参数类型的不同。
  • 重载的特点:与修饰符、返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
  • 重载方法调用:JVM通过方法的参数列表,调用匹配的方法。
    • 先找个数、类型最匹配的。
    • 再找个数和类型可以兼容的,如果同时多个方法可以兼容将会报错。

7.1.2 示例

举例1:

//System.out.println()方法就是典型的重载方法,其内部的声明形式如下:
public class PrintStream {
    public void println(byte x)
	public void println(short x)
	public void println(int x)
	public void println(long x)
	public void println(float x)
	public void println(double x)
	public void println(char x)
	public void println(double x)
	public void println()

}

public class HelloWorld{
    public static void main(String[] args) {
        System.out.println(3);
        System.out.println(1.2f);
        System.out.println("hello!");
    }
}

举例2:

//返回两个整数的和
public int add(int x,int y){
    return x+y;
}

//返回三个整数的和
public int add(int x,int y,int z){
    return x+y+z;
}
//返回两个小数的和
public double add(double x,double y){
    return x+y;
}

举例3:方法的重载和返回值类型无关

public class MathTools {
    //以下方法不是重载,会报错
    public int getOneToHundred(){
    	return (int)(Math.random()*100);
    }
    
    public double getOneToHundred(){
    	return Math.random()*100;
    }
}

7.2 可变个数的形参

在**JDK 5.0 中提供了Varargs(variable number of arguments)**机制。即当定义一个方法时,形参的类型可以确定,但是形参的个数不确定,那么可以考虑使用可变个数的形参。
格式:

方法名(参数的类型名 ...参数名)

举例:

//JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books);

//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String...books);

特点:

  1. 可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个。
  2. 可变个数形参的方法与同名的方法之间,彼此构成重载。
  3. 可变参数方法的使用与方法参数部分使用数组是一致的,二者不能同时声明,否则报错。
  4. 方法的参数部分有可变形参,需要放在形参声明的最后。
  5. 在一个方法的形参中,最多只能声明一个可变个数的形参。

7.3 方法的参数传递机制

7.3.1 形参和实参

  • 形参(formal parameter):在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参。
  • 实参(actual parameter):在调用方法时,方法名后面括号()中的使用的值/变量/表达式称为实际参数,简称实参。

7.3.2 参数传递机制:值传递

Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。

  • 形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参。
  • 形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参。

7.3.3 举例

  1. 形参是基本数据类型
    案例:编写方法,交换两个整型变量的值。
public class Test {
	public static void main(String[] args) {
		int m = 10;
		int n = 20;
		
		System.out.println("m = " + m + ", n = " + n);
		//交换m和n的值
//		int temp = m;
//		m = n;
//		n = temp;
		ValueTransferTest1 test = new ValueTransferTest1();
		test.swap(m, n);
		
		System.out.println("m = " + m + ", n = " + n);
	}
	public void swap(int m,int n){
		int temp = m;
		m = n;
		n = temp;
	}
}

内存解析:
在这里插入图片描述

  1. 形参是引用数据类型
public class Test {
	public static void main(String[] args) {
		
		Data d1 = new Data();
		d1.m = 10;
		d1.n = 20;
		
		System.out.println("m = " + d1.m + ", n = " + d1.n);
		
		//实现 换序
		
		ValueTransferTest2 test = new ValueTransferTest2();
		test.swap(d1);
		
		System.out.println("m = " + d1.m + ", n = " + d1.n);
		
	}
	
	public void swap(Data data){
		int temp = data.m;
		data.m = data.n;
		data.n = temp;
	}
}
class Data{
	int m;
	int n;
}

内存解析:
在这里插入图片描述

八 关键字:package、import

8.1 package(包)

package,称为包,用于指明该文件中定义的类、接口等结构所在的包。

8.1.1 语法格式

package 顶层包名.子包名 ;

package pack1.pack2;    //指定类PackageTest属于包pack1.pack2

public class PackageTest{
	public void display(){
		System.out.println("in  method display()");
	}
}

说明:

  • 一个源文件只能有一个声明包的package语句。
  • package语句作为Java源文件的第一条语句出现。若缺省该语句,则指定为无名包。
  • 包名,属于标识符,满足标识符命名的规则和规范(全部小写)、见名知意。
    • 包通常使用所在公司域名的倒置:com.atguigu.xxx。
    • 大家取包名时不要使用"java.xx"包
  • 包对应于文件系统的目录,package语句中用 “.” 来指明包(目录)的层次,每.一次就表示一层文件目录。
  • 同一个包下可以声明多个结构(类、接口),但是不能定义同名的结构(类、接口)。不同的包下可以定义同名的结构(类、接口)。

8.1.2 包的作用

  • 包可以包含类和子包,划分项目层次,便于管理。
  • 帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式。
  • 解决类命名冲突的问题。
  • 控制访问权限

8.1.3 应用举例

举例1:某航运软件系统包括:一组域对象、GUI和reports子系统。
在这里插入图片描述举例2:MVC设计模式
MVC是一种软件构件模式,目的是为了降低程序开发中代码业务的耦合度。

MVC设计模式将整个程序分为三个层次:视图模型(Viewer)层控制器(Controller)层,与数据模型(Model)层。这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。

视图层viewer:显示数据,为用户提供使用界面,与用户直接进行交互。
 >相关工具类   view.utils
 >自定义view  view.ui

控制层controller:解析用户请求,处理业务逻辑,给予用户响应
 >应用界面相关    controller.activity
 >存放fragment   controller.fragment
 >显示列表的适配器 controller.adapter
 >服务相关的        controller.service
 >抽取的基类        controller.base
    
模型层model:主要承载数据、处理数据
 >数据对象封装 model.bean/domain
 >数据库操作类 model.dao
 >数据库      model.db

在这里插入图片描述

8.1.4 JDK中主要的包介绍

  • java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread,提供常用功能。
  • java.net----包含执行与网络相关的操作的类和接口。
  • java.io ----包含能提供多种输入/输出功能的类。
  • java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
  • java.text----包含了一些java格式化相关的类。
  • java.sql----包含了java进行JDBC数据库编程的相关类/接口。
  • java.awt----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。

8.2 import(导入)

为了使用定义在其它包中的Java类,需用import语句来显式引入指定包下所需要的类。相当于import语句告诉编译器到哪里去寻找这个类

8.2.1 语法格式

import 包名.类名;

8.2.2 应用举例

import pack1.pack2.Test;   //import pack1.pack2.*;表示引入pack1.pack2包中的所有结构

public class PackTest{
	public static void main(String args[]){
		Test t = new Test();          //Test类在pack1.pack2包中定义
		t.display();
	}
}

8.2.3 注意事项

  • import语句,声明在包的声明和类的声明之间。

  • 如果需要导入多个类或接口,那么就并列显式多个import语句即可。

  • 如果使用a.*导入结构,表示可以导入a包下的所有的结构。举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。

  • 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。

  • 如果已经导入java.a包下的类,那么如果需要使用a包的子包下的类的话,仍然需要导入。

  • 如果在代码中使用不同包下的同名的类,那么就需要使用类的全类名的方式指明调用的是哪个类。

  • (了解)import static组合的使用:调用指定类或接口下的静态的属性或方法。

九 面向对象特征一:封装性(encapsulation)

9.1 为什么需要封装?

  • 我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内部的结构吗?有必要碰电动机吗?
  • 我要开车,我不需要懂离合、油门、制动等原理和维修也可以驾驶。
  • 客观世界里每一个事物的内部信息都隐藏在其内部,外界无法直接操作和修改,只能通过指定的方式进行访问和修改。
    随着我们系统越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要遵循“高内聚、低耦合”。

高内聚、低耦合是软件工程中的概念,也是UNIX 操作系统设计的经典原则。
内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。

而“高内聚,低耦合”的体现之一:

  • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
  • 低耦合:仅暴露少量的方法给外部使用,尽量方便外部调用。

9.2 何为封装性?

所谓封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或者对象开放,向没必要开放的类或者对象隐藏信息。

通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

9.3 Java如何实现数据封装

  • 实现封装就是控制类或成员的可见性范围。这就需要依赖访问控制修饰符,也称为权限修饰符来控制。

  • 权限修饰符:publicprotected缺省private。具体访问范围如下:

修饰符本类内部本包内其他包的子类其他包非子类
private×××
缺省××
protected×
public

具体修饰的结构:

  • 外部类:public、缺省。
  • 成员变量、成员方法、构造器、成员内部类:public、protected、缺省、private。
    在这里插入图片描述
    在这里插入图片描述

9.4 封装性的体现

9.4.1 成员变量/属性私有化

概述:私有化类的成员变量,提供公共的get和set方法,对外暴露获取和修改属性的功能。

实现步骤:
使用 private 修饰成员变量

private 数据类型 变量名 ;

代码如下:

public class Person {
    private String name;
  	private int age;
    private boolean marry;
}

提供 getXxx方法 / setXxx 方法,可以访问成员变量,代码如下:

public class Person {
    private String name;
  	private int age;
    private boolean marry;

	public void setName(String n) {
		name = n;
    }

    public String getName() {
        return name;
	}

    public void setAge(int a) {
        age = a;
    }

    public int getAge() {
        return age;
    }
    
    public void setMarry(boolean m){
        marry = m;
    }
    
    public boolean isMarry(){
        return marry;
    }
}

测试:

public class PersonTest {
    public static void main(String[] args) {
        Person p = new Person();

        //实例变量私有化,跨类是无法直接使用的
		/* p.name = "张三";
        p.age = 23;
        p.marry = true;*/

        p.setName("张三");
        System.out.println("p.name = " + p.getName());

        p.setAge(23);
        System.out.println("p.age = " + p.getAge());

        p.setMarry(true);
        System.out.println("p.marry = " + p.isMarry());
    }
}

成员变量封装的好处:

  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里面加入控制逻辑,限制对成员变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。
  • 便于修改,提高代码的可维护性。主要说的是隐藏的部分,在内部修改了,如果其对外可以的访问方式不变的话,外部根本感觉不到它的修改。例如:Java8->Java9,String从char[]转为byte[]内部实现,而对外的方法不变,我们使用者根本感觉不到它内部的修改。

十 类的成员之三:构造器(Constructor)

我们new完对象时,所有成员变量都是默认值,如果我们需要赋别的值,需要挨个为它们再赋值,太麻烦了。我们能不能在new对象时,直接为当前对象的某个或所有成员变量直接赋值呢?
可以,Java给我们提供了构造器(Constructor),也称为构造方法

10.1 构造器的作用

new对象,并在new对象的时候为实例变量赋值。

举例:Person p = new Person(“Peter”,15);

解释:如同我们规定每个“人”一出生就必须先洗澡,我们就可以在“人”的构造器中加入完成“洗澡”的程序代码,于是每个“人”一出生就会自动完成“洗澡”,程序就不必再在每个人刚出生时一个一个地告诉他们要“洗澡”了。

10.2 构造器的语法格式

[修饰符] class 类名{
    [修饰符] 构造器名(){
    	// 实例初始化代码
    }
    [修饰符] 构造器名(参数列表){
        // 实例初始化代码
    }
}

说明:

  1. 构造器名必须与它所在的类名必须相同。
  2. 它没有返回值,所以不需要返回值类型,也不需要void。
  3. 构造器的修饰符只能是权限修饰符,不能被其他任何修饰。比如,不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值。
    代码如下:
public class Student {
    private String name;
    private int age;

    // 无参构造
    public Student() {}

    // 有参构造
    public Student(String n,int a) {
        name = n;
        age = a;
    }

    public String getName() {
        return name;
    }
    public void setName(String n) {
        name = n;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int a) {
        age = a;
    }

    public String getInfo(){
        return "姓名:" + name +",年龄:" + age;
    }
}
public class TestStudent {
    public static void main(String[] args) {
        //调用无参构造创建学生对象
        Student s1 = new Student();

        //调用有参构造创建学生对象
        Student s2 = new Student("张三",23);

        System.out.println(s1.getInfo());
        System.out.println(s2.getInfo());
    }
}

10.3 使用说明

  1. 当我们没有显式的声明类中的构造器时,系统会默认提供一个无参的构造器并且该构造器的修饰符默认与类的修饰符相同。
    在这里插入图片描述
  2. 当我们显式的定义类的构造器以后,系统就不再提供默认的无参的构造器了。
  3. 在类中,至少会存在一个构造器。
  4. 构造器是可以重载的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1210314.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阶段七-Day02-SpringMVC

一、Restful请求格式 1. 介绍 Rest(Representational State Transfer&#xff1a;表现层状态转移)是一种软件架构风格&#xff0c;其核心是面向资源的一种设计。何为面向资源&#xff0c;意思是网络上的所有事物都可以抽象为资源&#xff0c;而每个资源都有唯一的资源标识&…

【数学建模】(1)层次分析法(AHP)

一.层次分析法的定义 层次分析法&#xff0c;简称AHP&#xff0c;是指将与决策总是有关的元素分解成目标、准则、方案等层次&#xff0c;在此基础之上进行定性和定量分析的决策方法&#xff0c;是一种层次权重决策分析方法。 层次分析法是一种主观赋值评价方法…

淘宝/京东/拼多多三方接口调用设计方案

在为第三方系统提供接口的时候&#xff0c;肯定要考虑接口数据的安全问题&#xff0c;比如数据是否被篡改&#xff0c;数据是否已经过时&#xff0c;数据是否可以重复提交等问题 在设计三方接口调用的方案时&#xff0c;需要考虑到安全性和可用性。以下是一种设计方案的概述&a…

盒子模型-详解

一、盒子模型组成 所谓盒子模型&#xff1a;就是把HTML页面中的布局元素看作是一个矩形的盒子也就是一个盛装内容的容器。css盒子模型本质是一个盒子&#xff0c;封装周围的HTML元素&#xff0c;包括边框、外边距、内边距和实际内容。 margin:外边距 用于控制盒子与盒子之间的…

【ROS导航Navigation】一 | 概述

目录 致谢&#xff1a;ROS赵虚左老师 一、【概述】二狗子找大水法 Navigation全图 二、【SLAM】即时定位与地图构建 三、【AMCL】自适应蒙特卡洛定位 四、【Move_base】路径规划 五、【cmd_vel】运动控制 六、环境感知 致谢&#xff1a;ROS赵虚左老师 Introduction A…

多行业用户齐聚,2023 IoTDB 用户大会详细议程更新!

上周我们官宣了 2023 IoTDB 用户大会举办的消息&#xff0c;获得了多方小伙伴们积极的响应&#xff0c;作为第一次线下大会&#xff0c;我们已经开始期待与大家线下相见&#xff01; 为了回应大家对于大会内容的期待&#xff0c;我们火速把更加详细的议程“搬运”来啦~ 20 位大…

【亚马逊云科技】使用Amazon Lightsail快速建站

写在前面&#xff1a;博主是一只经过实战开发历练后投身培训事业的“小山猪”&#xff0c;昵称取自动画片《狮子王》中的“彭彭”&#xff0c;总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域&#xff0c;如今终有小成…

Webpack Bundle Analyzer包分析器

当我们需要分析打包文件dist里哪些资源可以进一步优化时&#xff0c;就可以使用包分析器插件webpack-bundle-analyzer。NPM上的介绍是使用交互式可缩放树图可视化 webpack 输出文件的大小。 我的是vue2项目。 1、webpack-bundle-analyzer插件的安装 $ npm install --save-dev…

Python数据容器(集合)

集合 1.集合的定义2.集合中常用操作4.常用功能总结5.集合的特点6.练习 思考&#xff1f; 我们目前接触到了列表、元组、字符串三个数据容器了。基本满足大多数的使用场景。为何要学新的集合类型呢&#xff1f; 通过特性分析 列表可以修改、支持重复元素且有序元组、字符串不可修…

Scala---方法与函数

一、Scala方法的定义 有参方法&无参方法 def fun (a: Int , b: Int) : Unit {println(ab) } fun(1,1)def fun1 (a: Int , b: Int) ab println(fun1(1,2)) 注意点&#xff1a; 方法定义语法 用def来定义可以定义传入的参数&#xff0c;要指定传入参数的类型方法可以写返…

【Java】线程的调度、生命周期及状态转换

&#x1f33a;个人主页&#xff1a;Dawn黎明开始 &#x1f380;系列专栏&#xff1a;Java ⭐每日一句&#xff1a;夜色难免黑凉&#xff0c;前行必有曙光 &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️ ​ 文章目录 一.&…

3.1 Linux 前置知识

1、硬件 我们知道&#xff0c;组成计算机的硬件主要有“主机”和“输入/输出设备”。 主机包括机箱、电源、主板、CPU&#xff08;Central Processing Unit&#xff0c;中央处理器&#xff09;、内存、显卡、声卡、网卡、 硬盘、光驱等。输入/输出设备包括显示器、键盘、鼠标…

王道数据结构课后代码题p40 6.有一个带头结点的单链表L,设计一个算法使其元素递增有序 (c语言代码实现)

这一题其实用到了直接插入排序的思想 视频讲解在这里哦&#xff1a;&#x1f447; p40 第6题 王道数据结构课后代码题 c语言代码实现_哔哩哔哩_bilibili 本题代码为 void paixu(linklist* L)//对单链表内的元素排序 {lnode* p (*L)->next;lnode* pre *L;lnode* r p-&…

「 电商API接口系列之淘宝API接口调用 」

API从技术角度来说就是应用程序编程接口。通过API我们可以直接获取一些我们需要的数据结果&#xff0c;而不需要自己编写相应的程序&#xff0c;有点类似模块化调用函数&#xff0c;大大加快了我们编程的速度。当然这个数据传输是需要网络的&#xff0c;所以一般API的形式看起来…

论文十问:ResNet(Deep Residual Learning for Image Recognition)

文章目录 1. 论文试图解决什么问题?2. 这是否是一个新的问题?3. 这篇文章要验证一个什么科学假设?4. 有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一课题在领域内值得关注的研究员&#xff1f;5. 论文中提到的解决方案之关键是什么?6. 论文中的实验是如何设计的?…

stable diffusion comfyui的api使用教程

一、为什么要使用comfyui的api?对比webui的api&#xff0c;它有什么好处&#xff1f; 1、自带队列 2、支持websocket 3、无需关心插件是否有开放api接口&#xff0c;只要插件在浏览器中可以正常使用&#xff0c;接口就一定可以使用 4、开发人员只需关心绘图流程的搭建 5、切换…

【LeetCode刷题笔记】二叉树(二)

257. 二叉树的所有路径 解题思路: 1. DFS 前序遍历 ,每次递归将 当前节点的拼接结果 传递到 下一层 中,如果当前节点是 叶子节点 ,就将 当前拼接结果 收集答案并返回。 注意:路径path结果可以使用 String 来拼接,这样可以避免回溯处理。

Git 本地库基本教程

目录 一. Git 概述 1.1 何为版本控制 1.2 为什么需要版本控制 1.3 版本控制工具 1.3.1 集中式版本控制工具 1.3.2 分布式版本控制系统 1.4 Git简介 1.5 Git工作机制 1.6 Git 和代码托管中心 1.6.1 局域网 1.6.2 互联网 二. Git 安装 三. Git…

SystemC 学习之与 System Verilog 的混合仿真(九)

1、下载 uvmc (uvm connect) https://download.csdn.net/download/yp18792574062/88529417?spm1001.2014.3001.5501 2、配置相关环境变量 export UVM_HOME${VCS_HOME}/etc/uvm export UVMC_HOME/home/yangpan/yangpan/uvmc/uvmc-2.3.1 然后执行 source ~/.zshrc 更新 3、…

让公有云服务“宁安如梦”的“定心丸”在哪里?

电视剧《宁安如梦》正在热播中&#xff0c;该剧讲述了主人公在经历人生的重大风险后&#xff0c;重获新生再活一遍&#xff0c;以确定性的方式抵御和化解原有的重大风险。然而&#xff0c;在现实的生活中&#xff0c;却没有这样的重来机会。 2023年11月13日&#xff0c;Gartne…