问题:什么是面向对象编程?面向过程与面向对象有什么区别?
// 针对“人把大象装入冰箱”事件,分析面向对象与面向过程
面向过程:
人打开冰箱 -> 人将大象装入冰箱 -> 人关闭冰箱
面向对象:
三类:人类、大象类、冰箱类
人类 {
打开(冰箱) {
冰箱.开门();
}
装入(大象, 冰箱) {
大象.进入(冰箱);
}
关闭(冰箱) {
冰箱.关门();
}
}
大象类 {
进入(冰箱) {
// 具体实现
}
}
冰箱类 {
开门() {
// 具体实现
}
关门() {
// 具体实现
}
}
面向对象的思想概述:
面向对象的三大主线:
类(class)和对象(object)是面向对象的核心概念,类是对一类事物的描述,而对象是实际存在的该类事物的个体。面向对象程序设计的重点是类的设计,而定义类其实是定义类中的成员,即成员变量和成员方法。
Java代码是由诸多个不同功能的类构成的,而类主要是由属性、方法、构造器、代码块和内部类组成,属性对应类中的成员变量,行为对应类中的成员方法。
其中,Java类的语法格式如下:
// 修饰符public表示类可以被任意访问、类的正文要用"{}"括起来
修饰符 class 类名 {
属性声明;
方法声明;
}
附类与类之间的关系:关联关系、继承关系、聚合关系(聚集和组合)三种。
面向对象思想的落地法则:
.
类的实例化:
类名 对象名 = new 类名(); // 即new+构造器
内存的基本结构:
.
类对象的内存解析:
例如,Animal animal = new Animal();
语句的执行过程为:
属性声明的语法格式:
修饰符 数据类型 属性名 = 初值; // 权限修饰符有public、protected、private和缺省四种
成员变量与局部变量:
对比点 | 成员变量 | 局部变量 |
---|---|---|
基本定义 | 在方法体外、类体内声明的变量 | 在方法体内声明的变量 |
具体分类 | 类变量(以static修饰)、实例变量 | 形参、方法局部变量、代码块局部变量 |
声明形式 | 修饰符 数据类型 属性名 = 初值; | 数据类型 属性名 = 初值; //没有修饰符,默认与所在方法修饰符相同 |
作用区域 | 有,范围较大 | 有,范围较小 |
初始化值 | 不同数据类型有不同默认初始化值 | 无默认初始化值,必须显式赋值 |
内存存储 | 存放在堆空间中 | 存放在栈空间中 |
方法声明的语法格式:
// 返回值类型:return语句传递返回值,若没有返回值则用void
权限修饰符 返回值类型 方法名(参数列表) {
方法体语句;
}
注意:方法即提供某种功能的实现,方法内可以调用本类的其他属性或方法,但是不能在方法内再定义方法。
/**
* 方法的重载实例:与返回值类型无关,只与参数列表有关。
*/
//返回两个整数的和
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; }
定义可变个数的形参的方法:
/**
* 可变个数形参定义格式:“数据类型 ... 形参名” 或 “数据类型[] 形参名”,两者是一致的;
* 可变个数的形参的方法与同名的方法之间构成重载;
* 在一个方法中,最多可声明一个可变个数的形参,且其一定要声明在方法形参的最后。
*/
//采用数组形参来定义方法
public static void test(int a ,String[] books);
//以可变个数形参来定义方法
public static void test(int a ,String … books);
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;
}
子类继承父类后,若父类的方法对子类不适用,即可对父类的方法进行重写;其与方法重载的不同之处在于:
对比点 | 方法的重写 | 方法的重载 |
---|---|---|
类 | 在子类中重写父类的方法 | 在同一个类中 |
返回值类型 | 与父类方法的返回值类型相同 | 与被重载方法的返回值类型相同或不同均可 |
方法名 | 与父类方法的名称相同 | 与被重载方法的名称相同 |
参数列表 | 与父类方法的参数列表相同 | 与被重载方法的参数列表不同 |
构造器 | 不存在构造器的重写 | 构造器是可以重载的 |
构造器的声明格式:
// 构造器的作用:创建对象,并为所创建对象进行初始化;
权限修饰符 类名(参数列表) {
初始化语句; // 可为属性进行初始化操作
}
注意事项:
类对象属性赋值的先后顺序:
默认初始化 > 显式初始化、代码块初始化 > 构造器中赋值 > 通过“对象.方法”的方式为对象的属性赋值。
初始化块(代码块)的作用主要是为Java对象进行初始化,其与显示初始化同级别按代码的先后顺序执行。
初始化块的声明格式:
class className {
修饰符 { // 若有修饰符只能为static
初始化语句; // 为类属性进行初始化操作
}
// 其他属性或方法声明
}
静态代码块与非静态代码块:
对比点 | 静态代码块 | 非静态代码块 |
---|---|---|
修饰符 | static | 无 |
输出语句 | 其中可以有输出语句 | 其中可以有输出语句 |
初始化对象 | 类的属性和声明,除非静态的属性外 | 类的属性和声明 |
可调用对象 | 不可调用非静态的属性和方法 | 可调用静态的属性和方法 |
执行顺序 | 多个静态代码块从上到下执行,且先于非静态代码块 | 多个非静态代码块从上到下执行 |
执行次数 | 只执行一次 | 每次创建对象时即执行一次,且先与构造器执行 |
实例:继承类中静态代码块、非静态代码块和构造器的执行先后顺序。
在Java中,允许一个类的定义于另一个类内部,前者称为内部类,后者称为外部类。内部类分为成员内部类和局部内部类,成员内部类声明在类内方法外,而局部内部类声明在类内方法里。
作为外部类的成员:
作为类:
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);
}
}
局部内部类是指在类的方法中定义的内部类,其作用范围是该方法体;需要注意的是,局部内部类是方法的一部分,而非外部类的成员,故外部类不能访问该内部类,但该内部类可以访问当前代码块的常量以及此外部类的所有成员。
public Comparable getComparable() {
// 局部内部类
class MyComparable implements Comparable {
public int compareTo(java.lang.Object obj) {
return 0;
}
}
// 返回类的对象
return new MyComparable();
}
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
public Comparable getComparable() {
// 匿名内部类
return new Comparable {
public int compareTo(java.lang.Object obj) {
return 0;
}
};
}
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
}
}