Java面向对象基础:类与对象、构造方法、this、static、代码块及包机制。
面向对象概念
面向过程和面向对象
面向过程:主要注重“先做什么后做什么”。核心思想是先分析解决问题的步骤,然后用代码、方法把这些步骤一步一步的实现。代表性语言是C语言。
优点:性能比面向对象的语言高,适合开发单片机
缺点:只适合开发一些小型的、简单的产品,不易维护、不易扩展、不易复用
面向对象:主要注重宏观上的把控。更符合人们的思考方式,将复杂的问题简单化,将复杂的问题拆分成一个一个的小问题,这些小问题的处理上又是先干什么、后干什么。宏观上使用面向对象把控,微观上依然使用面向过程处理。
优点:易维护、易扩展、易复用。开发出的产品高内聚、低耦合。适合做一些大型的商业级应用,比如商城APP、社交软件
缺点:性能相较于面向过程偏低。
举例
如何盖房子
面向过程
- 设计房子
- 购买材料
- 雇佣工人
- 修建毛坯房
- 装修
- 入住
面向对象
- 设计师:设计房子
- 采购员:负责购买材料
- 包工头:负责雇佣工人
- 建筑工人:负责盖房子
- 装修工人:负责房屋装修
- 业主:负责入住
类和对象
类:类就是一类事物的抽象,抽象就是提取这一类事物共有的属性和行为。
比如学生都拥有姓名、年龄、学号等属性,有吃饭、睡觉、学习等行为。这些就可以抽象为一个学生类。
对象(实例):万物皆对象。生活中能够接触到的任何一个具体的事物都是一个对象。
我们可以将类看做成一个模板或者图纸,用户根据这个模板去填充某个具体事物的一些属性,从而创造出一个特指的事物,这个事物就是对象。
面向对象基础
类的创建
使用关键字 class 来定义一个类,一个java文件中可以同时定义多个class,但是public修饰的class只能有一个
1 | 修饰符 class 类名 { |
代码演示
1 | package unit1; |
类的组成
- 属性 field:
修饰符 数据类型 属性名 = 默认值; - 方法(行为) method:
修饰符 返回值类型 方法名(参数列表) {}
事实上,这里称之为“属性”是不准确的,严格上来说我们应该称之为成员变量或静态变量,方法包括成员方法、静态方法
成员方法不需要加static,而静态方法则需要加static
1 | package unit1; |
创建对象
对象(object)也叫作实例(instance)。要创建一个对象,就需要有一个类,使用 new 关键字来创建一个对象
1 | 类名 对象名 = new 类名(); |
可以使用 对象.成员变量 来访问这个对象的属性,可以使用 对象.成员方法名 来调用成员方法
代码演示
1 | package unit1; |
注意:成员变量隶属于对象,同一个类多个对象之间的成员变量值互不影响
成员变量默认值
在类中定义成员变量时可以不进行赋值(不进行显式初始化),JVM默认会对其进行隐式初始化
| 数据类型 | 默认值 |
|---|---|
| byte、short、int、long | 0 |
| float、double | 0.0 |
| char | \u0000 |
| boolean | false |
| 引用类型 | null |
1 | public static void main(String[] args) { |
对象内存分析

代码演示
1 | package unit1; |
随堂练习
1、 使用计算器(Calculator)完成加法和减法运算,并能显示出该计算器的品牌和尺寸。
计算器成员变量:品牌(brand)、尺寸(size)
计算器成方法:加法运算(add)、减法运算(sub)和显示计算机信息(show)
1 | package unit1; |
成员变量与局部变量的区别
局部变量是定义在方法或者代码块中的变量,成员变量则是定义在类中的变量,和成员方法处在同一层。
区别
- 定义位置不同
- 成员变量定义在类中,整个类中的所有成员方法都可以访问
- 局部变量定义在方法或代码块中,只有所属的区域有效
- 在内存中的位置不同
- 局部变量存在于栈内存的方法中
- 成员变量存在于堆内存的对象中
- 生命周期不同
- 成员变量随着对象的创建而存在,随着对象的销毁而销毁
- 局部变量随着所属区域的执行而存在,随着所属区域的执行完毕而销毁
- 初始化不同
- 成员变量有默认初始化值
- 局部变量没有默认初始化值
- 可用修饰符不同
- 成员变量可以被public、protected、private、static、final等修饰符修饰
- 局部变量只能被final修饰
当成员变量和局部变量名称冲突时,使用就近原则。先找局部变量,有就使用,没有就找成员变量。
匿名对象
通过使用new关键字来创建对象,一般还会创建一个引用用来接收这个对象,但有的时候也可以不去创建这个引用,此时创建出来的对象叫做匿名对象。
当我们只是想调用一次对象的成员方法时,可以使用匿名对象。一般情况下使用匿名对象是不去使用它的成员变量的,因为这样没有什么意义。
当一个方法中需要的参数是一个对象时,也可以使用匿名对象
1 | package unit1; |
构造方法
构造方法简介
构造方法也叫作构造器,用于给对象进行初始化操作
语法
1 | 修饰符 类名(参数列表) { |
- 构造方法的方法名必须与类名完全一致
- 构造方法不能定义返回值类型,方法体中不允许return具体的值
- 构造方法必须使用new关键字调用,是一种特殊的方法
1 | public Student() { |
问题:对象的创建完全是由构造方法实现的吗?
不是。构造方法是创建java对象的其中一步,对象的创建有以下几步
- 分配对象存储空间
- 将成员变量进行默认初始化
- 执行成员变量的显式初始化
- 执行构造方法
- 返回对象的地址,使用遍历接收
无参构造方法
如果类中没有定义构造方法,那么会默认提供一个无参构造方法

有参构造方法
有参构造方法一般用来给成员变量进行赋初值
1 | package constructor; |
- 如果在类中定义了有参构造,默认的无参构造就没有了
- 当局部变量和成员变量同名时,使用this关键字来区分成员变量
- 养成习惯,不要省略无参构造
构造方法重载
构造方法也可以进行重载,当new对象时,JVM会自动根据传参的数据类型、顺序、个数来匹配符合的构造方法
1 | package constructor; |
构造方法和成员方法的区别
- 定义语法区别
- 构造方法的方法名要与类名保持一致,并且不能定义返回值类型
- 成员方法方法名只需要符合标识符命名规范即可,必须定义返回值类型
- 调用时期区别
- 构造方法在对象创建时调用
- 成员方法在对象创建成功后调用
- 调用方式区别
- 构造方法用new关键字来调用
- 成员方法用对象来调用
- 调用次数区别(对于同一个对象)
- 成员方法在对象创建成功后,可以调用任意次数
- 构造方法只能在创建对象时调用一次
随堂练习
1、 定义一个坐标类(Point),用于表示二维空间中的一个坐标位置。通过坐标类的方法,实现计算两个坐标位置之间的距离(要求:使用构造方法)。
坐标类成员变量:X轴点(x)、Y轴点(y)
坐标类构造方法:Point(double x, double y)
坐标类成员方法:计算两个坐标位置之间的距离(v)
point类
1 | package constructor; |
测试类
1 | package constructor; |
Java中数据类型传递
Java中只有值传递。
1 | package constructor; |
交换、修改成功与否,取决于操作的是堆内存还是栈内存,这就证明了java中只有值传递
随堂练习
猜数字游戏:一个类 A 有一个成员变量 v,随机生成0-100之间的整数。 定义一个类,对 A 类的成员变量 v 进行猜。 如果大了则提示大了,小了则提示小了。 等于则提示猜测成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36package constructor.test;
import java.util.Scanner;
/**
* 猜数字游戏:一个类 A 有一个成员变量 v,随机生成0-100之间的整数。
* 定义一个类,对 A 类的成员变量 v 进行猜。
* 如果大了则提示大了,小了则提示小了。 等于则提示猜测成功
*/
public class Test1 {
public static void main(String[] args) {
A a = new A();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请猜一个数字:");
int num = sc.nextInt();
if(num > a.v) {
System.out.println("猜大了,请继续猜");
}else if(num < a.v) {
System.out.println("猜小了,请继续猜");
}else {
System.out.println("猜中了");
break;
}
}
}
}
class A {
int v;
public A() {
int num = (int) (Math.random() * 101);
this.v = num;
}
}请定义一个交通工具(Vehicle)的类其中有: 属性: 速度(speed)、 体积(size)等,方法:移动(move())、设置速度(setSpeed(int speed))、加速 speedUp()、减速 speedDown()等。最后在测试类 Vehicle 中的 main()中实例化一个交通工具对象并通过方法给它初始化 speed,size 的值并且通过打印出来。另外调用加速、减速的方法对速度进行改变。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53package constructor.test;
/**
* 2. 请定义一个交通工具(Vehicle)的类其中有:
* 属性: 速度(speed)、 体积(size)等,
* 方法:移动(move())、设置速度(setSpeed(int speed))、
* 加速 speedUp()、减速 speedDown()等。
* 最后在测试类 Vehicle 中的 main()中实例化一个交通工具对象
* 并通过方法给它初始化 speed,size 的值并且通过打印出来。
* 另外调用加速、减速的方法对速度进行改变。
*/
public class Test2 {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.size = 20;
vehicle.setSpeed(80);
vehicle.move();
vehicle.speedUp();
vehicle.move();
vehicle.speedDown();
vehicle.move();
}
}
class Vehicle {
int speed;
double size;
public Vehicle() {
}
public Vehicle(int speed, double size) {
this.speed = speed;
this.size = size;
}
public void move() {
System.out.println(size + "的车正在以每小时" + speed + "的速度行驶");
}
public void setSpeed(int speed) {
this.speed = speed;
System.out.println("速度设置成功,当前车速为:" + this.speed);
}
public void speedUp() {
this.speed++;
}
public void speedDown() {
this.speed--;
}
}定义一个圆类——Circle,在类的内部提供一个属性:半径(r),同时 提供 两个 方 法 : 计算 面积 ( getArea() ) 和 计算 周长(getPerimeter()) 。 通过两个方法计算圆的周长和面积并且对计算结果进行输出。最后定义一个测试类对 Circle 类进行使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35package constructor.test;
/**
* 定义一个圆类——Circle,在类的内部提供一个属性:半径(r),
* 同时 提供 两个 方 法 : 计算 面积 ( getArea() )
* 和 计算 周长(getPerimeter()) 。
* 通过两个方法计算圆的周长和面积并且对计算结果进行输出。
* 最后定义一个测试类对 Circle 类进行使用。
*/
public class Test3 {
public static void main(String[] args) {
Circle circle = new Circle(3);
double area = circle.getArea();
double perimeter = circle.getPerimeter();
System.out.println("半径为3的圆,面积为:" + area + ",周长为:" + perimeter);
}
}
class Circle {
double r;
public Circle(double r) {
this.r = r;
}
public double getArea() {
final double PI = 3.14;
return PI * r * r;
}
public double getPerimeter() {
final double PI = 3.14;
return PI * r * 2;
}
}定义一个网络用户类,要处理的信息有用户 ID、用户密码、 email 地址。在建立类的实例时把以上三个信息都作为构造函数的参数输入, 其中用户 ID 和用户密码是必填,email地址是用户 ID 加上字符串”@gameschool.com”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40package constructor.test;
/**
* 定义一个网络用户类,要处理的信息有用户 ID、用户密码、 email 地址。
* 在建立类的实例时把以上三个信息都作为构造函数的参数输入,
* 其中用户 ID 和用户密码是必填,
* 默认email地址是用户 ID 加上字符串"@gameschool.com"
*/
public class Test4 {
public static void main(String[] args) {
NetUser u1 = new NetUser("1", "admin", "123@qq.com");
u1.show();
NetUser u2 = new NetUser("2", "123456", null);
u2.show();
}
}
class NetUser {
String id;
String password;
String email;
public NetUser(String id, String password, String email) {
this.id = id;
this.password = password;
if(email == null) {
this.email = this.id + "@gameschool.com";
}else {
this.email = email;
}
}
public void show() {
System.out.println("用户id:" + this.id +
"用户密码:" + this.password +
"用户email:" + this.email);
}
}
this关键字
在创建一个对象成功后,虚拟机还会动态的分配一个引用,这个引用就指向当前对象,就是this关键字
this可以调用本类中的成员变量、成员方法,还可以在构造方法中调用其他的构造方法。
谁来调用,this就指谁。
调用成员变量
当局部变量和成员变量同名时,成员变量会被屏蔽,此时要访问成员变量就要使用 this.成员变量名 形式来进行调用。
1 | package thisstudy; |
调用成员方法
访问成员方法时可以使用 this.成员方法 来进行调用,也可以省略this,编译器会为我们加上。
1 | public void eat() { |
构造方法之间的调用
在一个类的构造方法内部,可以使用this关键字调用其他的构造方法,这样就可以降低代码的重复。
注意:
- this只能用在构造方法中来调用构造方法,且必须是第一行语句
- this在编译时根据参数列表来选择调用对应的构造方法
- this不能递归调用,防止死循环。
1 | public Person() { |
随堂练习
定义一个类 Calculaion, 其中包含四个方法: 加(add()) 、 减(sub()) 、乘(times()) 和除(div()) 。 创建一个具有 main()函数的类。 在 main()函数中创建一个 Calculation 的实例对象并对其中的方法进行调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34package thisstudy;
public class Calculation {
String brand;
int size;
public Calculation(String brand, int size) {
this.brand = brand;
this.size = size;
}
public int add(int num1, int num2) {
this.show();
return num1 + num2;
}
public int sub(int num1, int num2) {
this.show();
return num1 - num2;
}
public int times(int num1, int num2) {
this.show();
return num1 * num2;
}
public int div(int num1, int num2) {
this.show();
return num1 / num2;
}
public void show() {
System.out.println("欢迎使用"+this.brand+"计算器");
}
}创建一个空调,并调用制冷、制热、通风功能。空调包含的属性有品牌、匹数、温度,功能有加热、制冷、通风等功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38package thisstudy;
public class AirCondition {
String brand;
int power;
double temperature;
public AirCondition(String brand) {
this.brand = brand;
}
public AirCondition(String brand, int power) {
this(brand);
this.power = power;
}
public AirCondition(String brand, int power, double temperature) {
this(brand, power);
this.temperature = temperature;
}
public void cold() {
System.out.println("制冷功能已开启");
}
public void hot() {
System.out.println("制热功能已开启");
}
public void wind() {
System.out.println("通风功能已开启");
}
public void show() {
System.out.println("品牌:" + this.brand +
",匹数:" + this.power +
",温度:" + this.temperature);
}
}
static 关键字
static修饰的被称为静态的,static关键字可以用来修饰变量和方法、代码块。
静态变量
在类中,与成员变量平级,被static关键字修饰的变量我们称之为 静态变量或类变量
静态变量的特点
- 静态变量优先于对象存在的,当类被加载完毕后静态变量就存在了。
- 一个类中,静态变量只有一份,可以被该类和该类的所有对象所共享
- 我们可以使用 类名.静态变量 或者 对象.静态变量 来进行调用,建议使用前者。
Student类
1 | package staticstudy; |
测试类
1 | package staticstudy; |
图解静态变量

静态变量和成员变量区别
- 生命周期不同
- 成员变量随着对象的创建而创建,随着对象的销毁而销毁
- 静态变量随着类的第一次加载而存在,随着类的消失而消失
- 调用方式不同
- 成员变量使用对象.变量名调用
- 静态变量一般使用类名.变量名调用
- 数据存储位置不同
- 成员变量存储在堆内存中,属于对象
- 静态变量存在于方法区中,属于类
- 创建次数不同
- 成员变量每次创建一个对象都会创建出一份
- 静态变量只会创建一次,因为类只会被加载一次
静态方法
static修饰的方法也称之为 静态方法 或者 类方法
特点
- 静态方法优先于对象存在,随着类的加载就已经存在了
- 可以使用 类名.方法名() 进行调用,也可以使用 对象.方法名() 进行调用
Student类
1 | package staticstudy; |
调用
1 | package staticstudy; |
引申代码
1 | package staticstudy; |
静态方法和成员方法区别
- 访问变量区别
- 成员方法可以访问成员变量和静态变量
- 静态方法只能访问静态变量
- 访问方法区别
- 成员方法可以访问成员方法和静态方法
- 静态方法只能访问静态方法
- 访问this
- 成员方法中可以访问this
- 静态方法中不能访问this
简单记忆:static的只能调用static
代码块
广义上:代码块就是存在于类、方法中,使用大括号包裹起来的一段代码
狭义上:一般只认为类中用大括号括起来的属于代码块
静态代码块
在类中,与静态变量、静态方法平级的,使用static修饰的代码块叫做静态代码块,主要用于给类做初始化操作。
1 | public class Student { |
特点
- 静态代码块只能定义在类中,不能定义在方法中
- 一个类中允许有多个静态代码块,执行时按照定义的顺序执行
- 静态代码块中的遍历都是局部变量,访问只能访问类的静态变量,不能访问成员变量
- 静态代码块随着类的加载而自动执行,并且只会执行一次
静态代码块可以用于给静态变量赋初值,或者用于执行一些全局的初始化操作
构造代码块
在类中,与静态代码块平级,没有static修饰的代码块叫做构造代码块,主要用于给对象进行一些初始化
1 | public class Student { |
特点
- 构造代码块只能定义在类中,不能定义在方法中
- 一个类可以定义多个构造代码块,执行时按照定义的顺序执行
- 构造代码块中的变量都是局部变量,既可以访问静态变量也可以访问成员变量
- 构造代码块随着对象的创建而执行,每创建一个对象就执行一次
我们使用构造代码块一般用于给对象进行一些初始化操作
代码块执行顺序
Person类
1 | package staticstudy; |
测试类
1 | package staticstudy; |
结论:代码执行顺序按照 静态代码块->构造代码块->构造方法 的顺序进行执行
package包
package概述
为什么使用包?
包的作用是为我们管理java类,给类提供了多层命名空间。开发中,我们会遇到大量同名的类,通过包我们可以很容易解决类重名问题,也可以有效地对类进行管理。
包的命名规则
一般情况下,包名使用公司域名反着写,再加上模块名,比如 baidu.com
包名命名遵循标识符规则,一般只采用全部小写字母,多层包之间使用 . 进行连接。
包名不能以 java 开头
如何使用包
定义在指定包下的类,第一行必须要使用 package 关键字声明全包名
1 | package cn.pdd.product; |
类的访问
使用一个类时,需要进行导包操作。
1 | import 包名.类名; |
导包有两个特殊场景
- 当当前类与待使用类存在同一个包下时,可以不进行导包
- 当待使用类是
java.lang包下的,可以不进行导包
包在使用时还需要注意一些细节
- 当一个类中导入了两个同名的类时,其中一个必须使用 全类名 来展示,即 包名.类名