J2SE-3 面向对象编程之Java类及类的成员

问题:什么是面向对象编程?面向过程与面向对象有什么区别?

  • 面向过程与面向对象都是一种思想,后者是相对于前者而言的;
  • 前者强调的是功能行为,而后者将功能封装进对象,强调的是具备了功能的对象
  • 面向对象更加强调运用人类日常思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。
// 针对“人把大象装入冰箱”事件,分析面向对象与面向过程
面向过程:
    人打开冰箱 -> 人将大象装入冰箱 -> 人关闭冰箱

面向对象:
    三类:人类、大象类、冰箱类
    人类 {
        打开(冰箱) {
            冰箱.开门();
        }
        装入(大象, 冰箱) {
            大象.进入(冰箱);
        }
        关闭(冰箱) {
            冰箱.关门();
        }
    }
    大象类 {
        进入(冰箱) {
            // 具体实现
        }
    }
    冰箱类 {
        开门() {
            // 具体实现
        }
        关门() {
            // 具体实现
        }
    }

面向对象的思想概述:

  • 完成需求时,需要先看所要完成的功能对应的类的对象是否存在;
  • 若存在,则通过对象直接调用对应的类中的属性和方法,完成需求即可;
  • 若不存在,则需要创建类的对象,甚至说类都不存在,就需要设计类。

面向对象的三大主线:

  • Java类及类的成员:属性、方法、构造器、代码块和内部类;
  • 面向对象的三大特征:封装性(Encapsulation)、继承性(Inheritance)、多态性(Polymorphism);
  • 其他关键字:this、super、package、import、static、final、abstract、interface等。

1. Java类和对象

  类(class)和对象(object)是面向对象的核心概念,类是对一类事物的描述,而对象是实际存在的该类事物的个体。面向对象程序设计的重点是类的设计,而定义类其实是定义类中的成员,即成员变量和成员方法。

1.1 类

  Java代码是由诸多个不同功能的类构成的,而类主要是由属性方法构造器、代码块和内部类组成,属性对应类中的成员变量,行为对应类中的成员方法。
  其中,Java类的语法格式如下:

// 修饰符public表示类可以被任意访问、类的正文要用"{}"括起来
修饰符 class 类名 {
    属性声明;
    方法声明;
}

  附类与类之间的关系:关联关系、继承关系、聚合关系(聚集和组合)三种。

1.2 对象

  面向对象思想的落地法则:

  • 设计类,并设计类的成员(属性、方法、构造器等);
  • 通过类,创建类的对象,即类的实例化;
  • 通过“对象名.对象成员”的方式来调用其成员,以完成相应的功能。


  类的实例化:

类名 对象名 = new 类名(); // 即new+构造器

  内存的基本结构:

  • 栈空间(stack):存放局部变量、对象的引用名、数组的引用名;
  • 堆空间(heap):new出来的东西,即数组的实体、对象的实体,含成员变量;
  • 方法区:存放包名、类名和方法的定义,常量池(如字符串常量池);
  • 静态域:存放静态变量。

.
 类对象的内存解析:
  例如,Animal animal = new Animal();语句的执行过程为:

  • 在栈空间中,声明animal变量,用于存放对象的引用;
  • 在堆空间中,为所创建的对象开辟空间,利用构造器对其成员进行初始化;
  • 将所创建的对象在堆中开辟的空间首地址传递给栈空间中的引用变量animal;
  • 由此,即建立起栈空间中animal变量与堆空间中所创建对象之间的关系。

2. 类的成员之一:属性

  属性声明的语法格式:

修饰符 数据类型 属性名 = 初值; // 权限修饰符有public、protected、private和缺省四种

  成员变量与局部变量:

对比点 成员变量 局部变量
基本定义 在方法体外、类体内声明的变量 在方法体内声明的变量
具体分类 类变量(以static修饰)、实例变量 形参、方法局部变量、代码块局部变量
声明形式 修饰符 数据类型 属性名 = 初值; 数据类型 属性名 = 初值; //没有修饰符,默认与所在方法修饰符相同
作用区域 有,范围较大 有,范围较小
初始化值 不同数据类型有不同默认初始化值 无默认初始化值,必须显式赋值
内存存储 存放在堆空间中 存放在栈空间中

3. 类的成员之二:方法

  方法声明的语法格式:

// 返回值类型:return语句传递返回值,若没有返回值则用void
权限修饰符 返回值类型 方法名(参数列表) {
    方法体语句;
}

  注意:方法即提供某种功能的实现,方法内可以调用本类的其他属性或方法,但是不能在方法内再定义方法。

3.1 方法的重载

  • 要求:① 同一个类中的多个方法; ② 方法名必须相同; ③ 方法的参数列表不同,即参数个数和类型不同;
  • 注意:方法的重载与方法的返回值类型没有关系,只看参数列表的不同。
/**
 * 方法的重载实例:与返回值类型无关,只与参数列表有关。
 */
//返回两个整数的和
  int add(int x, int y ) { return x + y; }
//返回三个整数的和
  int add(int x, int y, int z ) { return x + y + z; }
//返回两个小数的和
  double add(double x, double y) { return x + y; }

3.2 可变个数的形参的方法

  定义可变个数的形参的方法:

/**
 * 可变个数形参定义格式:“数据类型 ... 形参名” 或 “数据类型[] 形参名”,两者是一致的;
 * 可变个数的形参的方法与同名的方法之间构成重载;
 * 在一个方法中,最多可声明一个可变个数的形参,且其一定要声明在方法形参的最后。
 */

//采用数组形参来定义方法
public static void test(int a ,String[] books);

//以可变个数形参来定义方法
public static void test(int a ,String … books);

3.3 Java的值传递机制

  Java中方法的参数传递方式为值传递机制,即

  • 若形参是基本数据类型的,则将实参的值传递给形参的基本数据类型的变量;
  • 若形参是引用数据类型的,则将实参的值(对应堆空间中对象实体的首地址)传递给形参的引用数据类型的变量。
  • 注意:方法中的变量均为局部变量,存放在内存的栈空间中。

  理解值传递的基本原理:

// 方式一:交换两个变量的值
int i = 10; // 栈空间中,变量i所指向的内存地址存放的数据为10
int j = 5;
int temp = i; // 基本数据类型:值传递,即传递的是10
i = j;
j = temp;
// 方式二:交换两个变量的值,该实现是错误的
public void swap(int i,int j){ // main方法中i=10,j=5
    int temp = i;
    i = j;
    j = temp; // main方法将变量值传递给形参,形参值互换后不影响实参值
}
// 方式三:交换两个变量的值
public void swap(DataSwap d){ // 传入的是对象的引用,即实参对象在堆空间的首地址
    int temp = d.i;
    d.i = d.j;
    d.j = temp;
}

3.4 方法的重写

  子类继承父类后,若父类的方法对子类不适用,即可对父类的方法进行重写;其与方法重载的不同之处在于:

对比点 方法的重写 方法的重载
在子类中重写父类的方法 在同一个类中
返回值类型 与父类方法的返回值类型相同 与被重载方法的返回值类型相同或不同均可
方法名 与父类方法的名称相同 与被重载方法的名称相同
参数列表 与父类方法的参数列表相同 与被重载方法的参数列表不同
构造器 不存在构造器的重写 构造器是可以重载的

4. 类的成员之三:构造器

  构造器的声明格式:

// 构造器的作用:创建对象,并为所创建对象进行初始化;
权限修饰符 类名(参数列表) {
    初始化语句; // 可为属性进行初始化操作
}

  注意事项:

  • 若设计类时不显式声明类的构造器,程序则会提供一个默认的无参的构造器;
  • 若设计类时显式声明了类的构造器,程序则不会提供一个默认的无参构造器;
  • 每一个Java类都至少有一个构造器,其不能被static、final、synchronized、abstract、native等关键字修饰,且多个构造器之间构成重载。

  类对象属性赋值的先后顺序:
  默认初始化 > 显式初始化、代码块初始化 > 构造器中赋值 > 通过“对象.方法”的方式为对象的属性赋值。


5. 类的成员之四:代码块

  初始化块(代码块)的作用主要是为Java对象进行初始化,其与显示初始化同级别按代码的先后顺序执行。
  初始化块的声明格式:

class className {

    修饰符 {   // 若有修饰符只能为static
        初始化语句; // 为类属性进行初始化操作
    }

    // 其他属性或方法声明
}

  静态代码块与非静态代码块:

对比点 静态代码块 非静态代码块
修饰符 static
输出语句 其中可以有输出语句 其中可以有输出语句
初始化对象 类的属性和声明,除非静态的属性外 类的属性和声明
可调用对象 不可调用非静态的属性和方法 可调用静态的属性和方法
执行顺序 多个静态代码块从上到下执行,且先于非静态代码块 多个非静态代码块从上到下执行
执行次数 只执行一次 每次创建对象时即执行一次,且先与构造器执行

  
  实例:继承类中静态代码块、非静态代码块和构造器的执行先后顺序。


6. 类的成员之五:内部类

  在Java中,允许一个类的定义于另一个类内部,前者称为内部类,后者称为外部类。内部类分为成员内部类和局部内部类,成员内部类声明在类内方法外,而局部内部类声明在类内方法里。

6.1 成员内部类

  作为外部类的成员:

  • 可以声明为final、abstract;
  • 和外部类不同,内部类可以使用四种权限修饰符进行修饰;
  • 可以声明为static的,但此时不可使用外层类的非static的成员。

  作为类:

  • 可以声明为abstract类,故可被其它的内部类所继承;
  • 注意:只有在外部类或static内部类中才可以声明static成员。
package com.whut.qiaobc.practice;

public class Animal {

    private int num = 66;

    // 非静态内部类
    public class Dog {
        private int num = 18;

        public void display(int num) {
            System.out.println(num);    // 2.1 局部变量num
            System.out.println(this.num);   // 2.2 内部类对象属性num
            System.out.println(Animal.this.num); // 2.3 外部类对象属性num
        }
    }

    // 静态内部类
    public static class Bird {
        public int num = 33;
    }

    public static void main(String[] args) {
        // 1.1 创建非静态内部类对象
        Animal animal = new Animal();
        Animal.Dog dog = animal.new Dog();
        dog.display(22);

        // 1.2 创建静态内部类对象
        Animal.Bird bird = new Animal.Bird();
        System.out.println(bird.num);
    }

}

6.2 局部内部类

  局部内部类是指在类的方法中定义的内部类,其作用范围是该方法体;需要注意的是,局部内部类是方法的一部分,而非外部类的成员,故外部类不能访问该内部类,但该内部类可以访问当前代码块的常量以及此外部类的所有成员。

public Comparable getComparable() {
    // 局部内部类
    class MyComparable implements Comparable {
        public int compareTo(java.lang.Object obj) {
            return 0;
        }
    }

    // 返回类的对象
    return new MyComparable();
}

6.3 匿名内部类

  匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。

public Comparable getComparable() {
    // 匿名内部类
    return new Comparable {
        public int compareTo(java.lang.Object obj) {
            return 0;
        }
    };
}

6.4 判断如下程序的输出结果

package com.whut.qiaobc.practice;

public class Test {

    public Test() {
        Inner s1 = new Inner();
        s1.a = 10;
        Inner s2 = new Inner();
        s2.a = 20;

        // 在外部类的构造器中,可以使用“new 外部类.内部类()”的方式创建内部类
        Test.Inner s3 = new Test.Inner();
        System.out.println(s3.a);   // 5
    }

    class Inner {
        public int a = 5;
    }

    public static void main(String[] args) {
        Test t = new Test();    // 输出5
        Inner r = t.new Inner();
        System.out.println(r.a);    // 输出5
    }
}

你可能感兴趣的:(J2SE-3 面向对象编程之Java类及类的成员)