第五章 面向对象基础篇(重点+难点)

1、面向对象编程概述

面向对象,是软件开发中的一类编程风格、开发范式。除了 面向对象 ,还有 面向过程 、 指令式编程 和函数式编程 。在所有的编程范式中,我们接触最多的还是面向过程和面向对象两种。
(1)面向过程的程序设计思想(POP)
  • 关注的焦点是 过程 :过程就是操作数据的步骤。如果某个过程的实现代码重复出现,那么就可以把这个过程抽取为一个 函数 。这样就可以大大简化冗余代码,便于维护。
  • 典型的语言:C语言
  • 代码结构:以 函数 为组织单位。是一种执行者思维 ,适合解决简单问题。扩展能力差、后期维护难度较大。

(2)面向对象的程序设计思想(OOP)

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

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

2.1类和对象概述

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

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

2.2、类的成员概述(本章目前只研究属性和方法)

引例:
(1)现实世界的生物体,大到鲸鱼,小到蚂蚁,都是由最基本的 细胞 构成的。同理, Java 代码世界是 由诸多个不同功能的 构成的。
(2)现实生物世界中的细胞又是由什么构成的呢?细胞核、细胞质、
(3)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:创建对象

语法格式:

// 方式 1 :给创建的对象命名
// 把创建的对象用一个引用数据类型的变量保存起来,这样就可以反复使用这个对象了
类名 对象名 = new 类名 ();
// 方式 2
new 类名 () // 也称为匿名对象

举例:

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

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

  • 对象是类的一个实例,必然具备该类事物的属性和行为(即方法)。
  • 使用" 对象名.属性 " " 对象名.方法 "的方式访问对象成员(包括属性和方法)

举例:

//声明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.4匿名对象

(1)定义:不定义对象的句柄,直接调用对象的方法,这样的对象叫做匿名对象。

举例:new Person().shout();

(2)使用场景:

(i)如果一个对象只需要进行一次方法调用,那么可以使用匿名对象。

(ii)匿名对象常用作实参传递给一个方法调用。

3、对象的内存解析

说明:

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

面试题:对象名中存储的是什么?

答:对象地址。

public class StudentTest{
    public static void main(String[] args){
        System.out.println(new Student());//Student@7852e922
        Student stu = new Student();
        System.out.println(stu);//Student@4e25154f
        int[] arr = new int[5];
        System.out.println(arr);//[I@70dea4e
    }
}

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

4、类的成员——成员变量(field)

4.1 如何声明成员变量

  • 语法格式:

[修饰符1] class 类名{

        [修饰符2] 数据类型 成员变量名 [ = 初始化值];

}

  • 说明:
    • 位置要求:必须在类中,方法外
    • 修饰符2(暂不考虑)
      • 常用的权限修饰符有:private、缺省、protectedpublic
      • 其他修饰符:staticfinal
  • 数据类型
    • 任何基本数据类型(intBoolean) 或 任何引用数据类型。
  • 成员变量名
    • 属于标识符,符合命名规则和规范即可。
  • 初始化值
    • 根据情况,可以显式赋值;也可以不赋值,使用默认值

 举例:

public class Person{

    private int age;
    public String name;
}

4.2成员变量和局部变量

(1)变量的分类:成员变量和局部变量

  • 在方法体外,类体内声明的变量称为成员变量。
  • 在方法体内声明的变量称为局部变量。
  •  其中,static可以将成员变量分为两大类,静态变量和非静态变量。其中静态变量又称为类变量,非静态变量又称为实例变量或者属性。接下来先学习实例变量。 

 

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

(3)对象属性的默认初始化值

当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。

5、类的成员——方法

5.1如何声明方法

(1)方法声明的格式

[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表]{

        方法体;

}

注意事项:
  • 一个完整的方法 = 方法头 + 方法体。
    • 方法头就是 [修饰符] 返回值类型 方法名([形参列表])[throws 异常列表] ,也称为 方法签名 。通常调用方法时只需要关注方法头就可以,从方法头可以看出这个方法的功能和调用格式。
    • 方法体就是方法被调用后要执行的代码。对于调用者来说,不了解方法体如何实现的,并不影响方法的使用。
  •  方法头可能包含5个部分
    • 修饰符:可选的。方法的修饰符也有很多,例如:public、protected、private、static、abstract、native、final、synchronized等,后面会学习。
      • 其中,权限修饰符有public、protected、private。在讲封装性之前,我们先默认使用pulbic修 饰方法。
      • 其中,根据是否有static,可以将方法分为静态方法和非静态方法。其中静态方法又称为类方 法,非静态方法又称为实例方法。咱们在讲static前先学习实例方法。
    • 返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者。
      • 无返回值,则声明:void
      • 有返回值,则声明出返回值类型(可以是任意类型)。与方法体中“ return 返回值 ”搭配使 用 。
    • 方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意”
    • 形参列表:表示完成方法体功能时需要外部提供的数据列表。可以包含零个,一个或多个参数。
      (i)无论是否有参数,()不能省略,(ii)如果有参数,每一个参数都要指定数据类型和参数名,多个参数之间使用逗号分隔,例如:一个参数: (数据类型 参数名) 二个参数: (数据类型1 参数1, 数据类型2 参数2)。(iii)参数的类型可以是基本数据类型、引用数据类型。
    • throws 异常列表:可选,在异常处理章节再讲 。
  • 方法体: 方法体必须有 {} 括起来,在 {} 中编写完成方法功能的代码
  • 关于方法体中return语句的说明:
    • return 语句的作用是结束方法的执行,并将方法的结果返回去
    • 如果返回值类型不是 void ,方法体中必须保证一定有 return 返回值 ; 语句,并且要求该返回值结果的 类型与声明的返回值类型一致或兼容。
    • 如果返回值类型为 void 时,方法体中可以没有 return 语句,如果要用 return 语句提前结束方法的执 行,那么 return 后面不能跟返回值,直接写 return ; 就可以。
    • return 语句后面就不能再写其他代码了,否则会报错: Unreachable code

举例说明:

package com.atguigu.test04.method;
/**
* 方法定义案例演示
*/
    public class MethodDefineDemo {
/**
* 无参无返回值方法的演示
*/
    public void sayHello(){
        System.out.println("hello");
    }
  /**
    * 有参无返回值方法的演示
    * @param length int 第一个参数,表示矩形的长
    * @param width int 第二个参数,表示矩形的宽
    * @param sign char 第三个参数,表示填充矩形图形的符号
    */
    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.print(sign);
            }
            System.out.println();
        }
    }
    /**
    * 无参有返回值方法的演示
    * @return
    */
    public int getIntBetweenOneToHundred(){
         return (int)(Math.random()*100+1);
    }
    /**
    * 有参有返回值方法的演示
    * @param a int 第一个参数,要比较大小的整数之一
    * @param b int 第二个参数,要比较大小的整数之二
    * @return int 比较大小的两个整数中较大者的值
    */
    public int max(int a, int b){
        return a > b ? a : b;
    }
}

5.2如何调用实例方法

方法是通过方法名被调用,且只有被调用才会执行。
(1)方法调用的格式
对象 . 方法名 ([ 实参列表 ])

(2) 举例说明

package com.atguigu.test04.method;
/**
* 方法调用案例演示
*/
    public class MethodInvokeDemo {
        public static void main(String[] args) {
            //创建对象

            MethodDefineDemo md = new MethodDefineDemo();
            System.out.println("--------方法调用演示--------");

            //调用MethodDefineDemo类中无参无返回值的方法sayHello
            md.sayHello();
            md.sayHello();
            md.sayHello();

            //调用一次,执行一次,不调用不执行
            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(); //对象.非静态方法()

注意:

  • 必须先声明后使用,且方法必须定义在类的内部。
  • 调用一次就执行一次,不调用不执行。
  • 方法中可以调用类中的方法或属性,不可以在方法内部定义方法。
//正确示例
类A {
        方法1 (){
        }
        方法2 (){
        }
}

//错误示例:方法内部定义方法

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

5.3关键字return的使用

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

5.4方法调用内存解析

(1)方法 没有被调用 的时候,都在 方法区 中的字节码文件(.class)中存储。
(2)方法 被调用 的时候,需要进入到 栈内存 中运行。方法每调用一次就会在栈中有一个 入栈 动作,即 给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值。
(3)当方法执行结束后,会释放该内存,称为 出栈 ,如果方法有返回值,就会把结果返回调用处,如 果没有返回值,就直接结束,回到调用处继续执行下一条指令。
(4)栈结构:先进后出,后进先出。

6对象数组

        数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用类型中的类时,我们称为对象数组。
        引例1
定义类 Student 1,包含三个属性:学号 number(int) ,年级 state(int) ,成绩 score(int) 。 创建 20 个学生对象,
学号为 1 20 ,年级和成绩都由随机数确定。
问题一:打印出 3 年级 (state 值为 3 )的学生信息。
问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
提示:
1) 生成随机数: Math.random() ,返回值类型 double;
2) 四舍五入取整: Math.round(double d) ,返回值类型 long
public class Student1 {
    //属性
    private int number;
    private int state;
    private int score;

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public String getInfo(){
        return "学号:" + this.number + ",年级:"+this.state+",成绩:"+this.score;
    }
}
public class Student1Test {
    public static void main(String[] args) {
//        创建20个学生对象,学号为1到20,年级和成绩都由随机数确定
        Student1[] arr = new Student1[20];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = new Student1();
            arr[i].setNumber((int)(Math.random()*(20 - 1 - 1)) +1);
            arr[i].setState((int)(Math.random()*(6 - 1 + 1))+1);
            arr[i].setScore((int)(Math.random()*(101 - 1 + 1)));
        }
//        问题一:打印出3年级(state值为3)的学生信息。
        for (int i = 0; i < arr.length; i++) {
            if(arr[i].getState() == 3){
                System.out.println(arr[i].getInfo());
            }
        }
//       问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - i - 1; j++) {
                if(arr[j].getScore()>arr[j+1].getScore()){
                    Student1 temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i].getInfo());
        }
    }
}

注意:在对引用数据类型进行冒泡排序时易错点,这里是交换了成绩,并没有换位置。

if(arr[j].getScore()>arr[j+1].getScore()){
                  int temp = arr[j],getScore();
                    arr[j],getScore() = arr[j+1],getScore();
                    arr[j+1],getScore() = temp;
                }

对象数组的内存解析

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

7、再谈方法

7.1方法的重载(面试高频考点)

(1)概念及特点

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

(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()
}

举例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;
    }
}
练习 判 断与 void show(int a,char b,double c){} 构成重载的有:
a)void show(int x,char y,double z){} // no
b)int show(int a,double c,char b){} // yes
c) void show(int a,double c,char b){} // yes
d) boolean show(int c,char b){} // yes
e) void show(double c){} // yes
f) double show(int x,char y,double z){} // no
g) void shows(){double c} // no

7.2 可变个数的形参

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

(1)格式

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

(2)举例:

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

(3)特点:

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

 举例1:n个字符串进行拼接,每一个字符串之间使用某字符进行分割,如果没有传入字符串,那么返回空字符串""

public class StringTools {
    String concat(char seperator, String... args){
    String str = "";
        for (int i = 0; i < args.length; i++) {
            if(i==0){
                str += args[i];
            }else{
            str += seperator + args[i];
            }
        }
        return str;
    }
}

7.3 方法的参数传递机制

(1)形参与实参

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

(2)参数传递机制:值传递

Java 里方法的参数传递方式只有一种: 值传递 。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
  • 形参是基本数据类型:将实参基本数据类型变量的“数据值传递给形参 。
  • 形参是引用数据类型:将实参引用数据类型变量的“地址值传递给形参

(3)举例

(i)形参是基本数据类型(经典例题)

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;
    }
}

内存解析:

(ii)形参是引用数据类型

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;
}

内存解析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值