1. 面向过程与面向对象
- 面向过程
- 面向对象
2. 面向对象的思想概述
- 类(Class)和对象(Object)是面向对象的核心概念
- 类是对一类事物的描述,是抽象的、概念上的定义
- 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)
- 属性:对应类中的成员变量
- 行为:对应类中的成员方法
2.1 类的语法格式
修饰符 class 类名 {
\qquad
属性声明;
\qquad
方法声明;
}
说明: 修饰符public:类可以被任意访问,类的正文要用{ }括起来。
2.2 对象的创建和使用
- 创建对象语法: 类名 对象名 = new 类名();
- 使用“对象名.对象成员”的方式访问对象成员(包括属性和方法)
如果创建了一个类的多个对象,对于类中定义的属性,每个对象都拥有各自的一套副本,且互不干扰
-
类的访问机制
- 在一个类中的访问机制: 类中的方法可以直接访问类中的成员变量。(例外: static方法访问非static, 编译不通过。 )
- 在不同类中的访问机制: 先创建要访问类的对象, 再用对象访问类中定义的成员
-
对象的产生图解
-
对象的使用图解
2.3 内存解析
- 堆( Heap) , 此内存区域的唯一目的就是存放对象实例, 几乎所有的对象实例都在这里分配内存。 这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配
- 栈( Stack) , 是指虚拟机栈。 虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型( boolean、 byte、char 、 short 、 int 、 float 、 long 、double) 、 对象引用( reference类型,它不等同于对象本身, 是对象在堆内存的首地址) 。 方法执行完, 自动释放。
- 方法区(Method Area) , 用于存储已被虚拟机加载的类信息、 常量、 静态变量、 即时编译器编译后的代码等数据。
2.4 匿名对象
- 我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。如: new Person().shout();
- 使用情况
- 如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象
- 我们经常将匿名对象作为实参传递给一个方法调用
2.5 类的成员之一:属性(field)
- 语法格式:
- 修饰符 数据类型 属性名 = 初始化值 ;
- 说明1: 修饰符
- 常用的权限修饰符有: private、缺省、 protected、 public
- 其他修饰符: static、 final (暂不考虑)
- 说明2:数据类型
- 任何基本数据类型(如int、 Boolean) 或 任何引用数据类型
- 说明3:属性名
- 属于标识符,符合命名规则和规范即可
2.6 类的成员之二:方法(method)
- 什么是方法(method、函数)
- 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程
- 将功能封装为方法的目的是,可以实现代码重用,简化代码
- Java里的方法不能独立存在,所有的方法必须定义在类里
- 方法的声明格式
修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ….){
\qquad 方法体程序代码
\qquad return 返回值;
}
2.6.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;}
2.6.2 可变形参的方法
- 说明
- 声明格式: 方法名(参数的类型名 …参数名)
- 可变参数:方法参数部分指定类型的参数个数是可变多个: 0个, 1个或多个
- 可变个数形参的方法与同名的方法之间,彼此构成重载
- 可变参数方法的使用与方法参数部分使用数组是一致的
- 方法的参数部分有可变形参,需要放在形参声明的最后
- 在一个方法的形参位置,最多只能声明一个可变个数形参
2.6.3 方法参数的值传递机制
方法,必须由其所在类或对象调用才有意义。若方法含有参数
- 形参:方法声明时的参数
- 实参: 方法调用时实际传给形参的参数值
Java的实参值如何传入方法呢?
Java里方法的参数传递方式只有一种: 值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响
- 形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
public class UntifaTest {
public static void main(String[] args) {
int x = 5;
System.out.println("修改之前x = " + x);// 5
// x是实参
change(x);
System.out.println("修改之后x = " + x);// 5
}
public static void change(int x) {
System.out.println("change:修改之前x = " + x);
x = 3;
System.out.println("change:修改之后x = " + x);
}
}
-
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
-
例子1
public class UntifaTest {
public static void main(String[] args) {
Person obj = new Person();
obj.age = 5;
System.out.println("修改之前age = " + obj.age);// 5
// x是实参
change(obj);
System.out.println("修改之后age = " + obj.age);// 3
}
public static void change(Person obj) {
System.out.println("change:修改之前age = " + obj.age);
obj.age = 3;
System.out.println("change:修改之后age = " + obj.age);
}
static class Person {
int age;
}
}
结果:
内存图解析:
- 例子2
public class UntifaTest {
public static void main(String[] args) {
Person obj = new Person();
obj.age = 5;
System.out.println("修改之前age = " + obj.age);// 5
// x是实参
change(obj);
System.out.println("修改之后age = " + obj.age);// 3
}
public static void change(Person obj) {
obj = new Person();
System.out.println("change:修改之前age = " + obj.age);
obj.age = 3;
System.out.println("change:修改之后age = " + obj.age);
}
static class Person {
int age;
}
}
结果:
内存图解析:
- 例子3
public class UntifaTest {
public static void main(String[] args) {
UntifaTest test = new UntifaTest();
int a = 5;
int b = 10;
test.swap(a, b);
System.out.println("交换结束后,变量a的值是" + a + ";变量b的值是" + b);
}
public void swap(int a, int b) {
int tmp = a;
a = b;
b = tmp;
System.out.println("swap方法里, a的值是" + a + "; b的值是" + b);
}
}
结果:
内存分析:略
- 例子4
public class TransferTest2 {
public static void swap(DataSwap ds) {
int temp = ds.a;
ds.a = ds.b;
ds.b = temp;
System.out.println("swap方法里, a Field的值是" + ds.a + ";b Field的值是" + ds.b);
}
static class DataSwap {
public int a;
public int b;
}
}
public class UntifaTest {
public static void main(String[] args) {
TransferTest2.DataSwap ds = new TransferTest2.DataSwap();
ds.a = 5;
ds.b = 10;
swap(ds);
System.out.println("交换结束后, a Field的值是" + ds.a + ";b Field的值是" + ds.b);
}
}
结果:
- 例子5
public class TransferTest3 {
public static void main(String args[]) {
TransferTest3 test = new TransferTest3();
test.first();
}
public void first() {
int i = 5;
Value v = new Value();
v.i = 25;
second(v, i);
System.out.println(v.i);
}
public void second(Value v, int i) {
i = 0;
v.i = 20;
Value val = new Value();
v = val;
System.out.println(v.i + " " + i);
}
static class Value {
int i = 15;
}
}
2.6.4 递归方法
一个方法体内调用它自身
- 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制
- 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
public class UntifaTest {
private static int count = 0;
public static void main(String[] args) {
recursion(10);
}
public static int recursion(int k) {
count++;
System.out.println("Count: " + count + " k: " + k);
if (k <= 0) {
return 0;
}
return recursion(k - 1) + recursion(k - 2);
}
}
结果:
思路:
2.7 封装与隐藏
中通过将数据声明为私有的(private), 再提供公共的( public)方法:getXxx()和setXxx()实现对该属性的操作, 以实现下述目的:
- 隐藏一个类中不需要对外提供的实现细节
- 使用者只能通过事先定制好的方法来访问数据, 可以方便地加入控制逻辑,限制对属性的不合理操作
- 便于修改, 增强代码的可维护性
2.8 构造器
-
构造器的特征
- 它具有与类相同的名称
- 它不声明返回值类型
- 不能被static、 final、 synchronized、 abstract、 native修饰,不能有return语句返回值
-
构造器的作用
创建对象;给对象进行初始化
-
语法格式
\qquad 修饰符 类名 (参数列表) {
\qquad 初始化语句;
} -
根据参数不同,构造器可以分为如下两类
- 隐式无参构造器(系统默认提供)
- 显式定义一个或多个构造器(无参、有参)
注 意:
Java语言中,每个类都至少有一个构造器
默认构造器的修饰符与所属类的修饰符一致
一旦显式定义了构造器, 则系统不再提供默认构造器
一个类可以创建多个重载的构造器
父类的构造器不可被子类继承
- 构造器重载
- 构造器一般用来创建对象的同时初始化对象
- 构造器重载,参数列表必须不同
- 构造器重载使得对象的创建更加灵活,方便创建各种不同的对象
例如:
public class Person{
\qquad public Person(String name, int age, Date d) {this(name,age);…}
\qquad public Person(String name, int age) {…}
\qquad public Person(String name, Date d) {…}
\qquad public Person(){…}
}
2.9 属性的赋值过程
截止到目前,我们讲到了很多位置都可以对类的属性赋值。现总结这几个位置,并指明赋值的先后顺序。
- 赋值的位置
- ① 默认初始化
- ② 显式初始化 / ⑤在代码块中赋值
- ③ 构造器中初始化
- ④ 通过“对象.属性“或“对象.方法”的方式赋值
- 赋值的先后顺序
- ① - ② / ⑤ - ③ - ④
- ②和⑤谁先谁后,那就看谁先写了
2.10 JavaBean及UML类图
- JavaBean是一种Java语言写成的可重用组件
- 所谓javaBean,是指符合如下标准的Java类:
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,且有对应的get、 set方法
2.11 this
-
它在方法内部使用,即这个方法所属对象的引用
-
它在构造器内部使用,表示该构造器正在初始化的对象
-
this 可以调用类的属性、方法和构造器
-
什么时候使用this关键字呢
- 当在方法内需要用到调用该方法的对象时,就用this
- 我们可以用this来区分属性和局部变量,例如:this.name = name
注意:
\qquad 可以在类的构造器中使用"this(形参列表)"的方式,调用本类中重载的其他的构造器!
\qquad 明确:构造器中不能通过"this(形参列表)“的方式调用自身构造器
\qquad 如果一个类中声明了n个构造器,则最多有 n - 1个构造器中使用了"this(形参列表)”
\qquad "this(形参列表)"必须声明在类的构造器的首行!
\qquad 在类的一个构造器中,最多只能声明一个"this(形参列表)
2.12 package、import
-
package
package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。 (若缺省该语句,则指定为无名包)。
它的格式为:package 顶层包名.子包名; -
包对应于文件系统的目录, package语句中,用 “.” 来指明包(目录)的层次;
-
包通常用小写单词标识。通常使用所在公司域名的倒置: com.atguigu.xxx
-
import
为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类(.*)。 import语句告诉编译器到哪里去寻找类。
语法格式:import 包名. 类名;