Java面向对象编程入门:核心概念
一、类与对象的核心定义
1.1 对象的本质
在Java中,对象是一种结构化的数据载体,本质是用于描述一个具体事物的专属数据结构,它可以完整存储一个事物的相关属性数据,并承载该事物对应的行为能力。
在实际开发中,无论是现实世界中的人物、商品,还是业务系统中的用户、订单、电影,都可以被抽象为对象,通过对象来统一管理其相关数据与行为。
1.2 类与对象的关系
类是对象的模板(也称为设计图),它定义了一类事物所具备的通用属性与行为;而对象是类的实例化产物,通过类可以创建出多个具备相同特征的独立对象。
简单来说,类是对一类事物的抽象描述,对象是该类事物的具体实例。例如:“明星”是一个类,而赵丽颖、杨幂就是该类的具体对象;“学生”是一个类,而播妞、播仔就是该类的具体对象。
1.3 类的基础语法
一个标准的类主要由两部分组成:
- 成员变量(属性):用于描述事物的特征数据;
- 成员方法(行为):用于描述事物的行为能力,即对数据的处理逻辑。
类的基础定义语法如下:
/**
* 明星类(模板)
* 定义了明星这类事物的通用属性
*/
public class Star {
// 成员变量(属性)
String name; // 姓名
int age; // 年龄
double height; // 身高
double weight; // 体重
}1.4 对象的创建与使用
在Java中,通过new关键字可以实例化类,得到一个具体的对象。每执行一次new操作,都会在内存中创建一个全新的独立对象。
对象创建完成后,可以通过对象名.属性名的方式为属性赋值,也可以通过对象名.方法名()的方式调用对象的行为方法。
public class StarTest {
public static void main(String[] args) {
// 实例化Star类,创建第一个明星对象
Star s1 = new Star();
// 为对象属性赋值
s1.name = "赵丽颖";
s1.age = 36;
s1.height = 165.0;
s1.weight = 44.6;
// 实例化Star类,创建第二个明星对象
Star s2 = new Star();
s2.name = "杨幂";
s2.age = 37;
s2.height = 166.5;
s2.weight = 45.0;
}
}1.5 带行为方法的类定义
类中不仅可以定义属性,还可以定义处理属性数据的行为方法。以学生类为例,我们可以在类中定义计算总成绩、平均成绩的方法,实现“谁的数据谁处理”的面向对象核心思想。
/**
* 学生类
* 包含学生属性与成绩处理的行为方法
*/
public class Student {
// 成员变量(属性)
String name; // 学生姓名
double chinese; // 语文成绩
double math; // 数学成绩
// 成员方法(行为):打印总成绩
public void printTotalScore() {
System.out.println(name + "同学的各科总分是:" + (chinese + math));
}
// 成员方法(行为):打印平均成绩
public void printAverageScore() {
System.out.println(name + "同学的各科平均分是:" + (chinese + math) / 2.0);
}
}方法的调用示例:
public class StudentTest {
public static void main(String[] args) {
// 创建学生对象并赋值
Student s1 = new Student();
s1.name = "播妞";
s1.chinese = 100;
s1.math = 100;
// 调用对象的行为方法
s1.printTotalScore();
s1.printAverageScore();
Student s2 = new Student();
s2.name = "播仔";
s2.chinese = 59;
s2.math = 100;
s2.printTotalScore();
s2.printAverageScore();
}
}执行结果:
播妞同学的各科总分是:200.0
播妞同学的各科平均分是:100.0
播仔同学的各科总分是:159.0
播仔同学的各科平均分是:79.51.6 对象的内存模型
Java程序运行时,对象的内存分配主要涉及三块内存区域:
- 栈内存:存储对象的引用变量(即对象的内存地址),方法执行时的局部变量也存放在栈中;
- 堆内存:存储
new关键字创建的对象本身,包括对象的所有成员变量; - 方法区:存储类的字节码文件(
.class),包括类的成员变量定义、方法定义等信息。
当执行Student s1 = new Student();时,会在堆内存中创建Student对象的实例,在栈内存中创建引用变量s1,s1中存储的是堆内存中对象的内存地址,通过该地址实现对对象属性和方法的访问。
二、构造器
2.1 构造器的定义与作用
构造器(也称为构造方法)是类中一种特殊的方法,它在使用new关键字创建对象时会被自动调用,核心作用是完成对象的初始化操作,最常见的场景是在创建对象的同时为成员变量赋值。
2.2 构造器的语法规范
构造器的定义需要遵循以下语法规则:
- 构造器的名称必须与类名完全一致;
- 构造器没有返回值类型,连
void都不能写; - 构造器可以定义参数,也可以无参数。
基础语法如下:
public class Student {
String name;
int age;
// 无参构造器
public Student() {
// 初始化逻辑
}
// 有参构造器
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}2.3 构造器的使用注意事项
- 默认无参构造器:任何一个类在定义时,即使不手动编写构造器,Java编译器都会自动为该类生成一个默认的无参构造器;
- 构造器的覆盖规则:如果手动为类定义了有参构造器,编译器将不再生成默认的无参构造器。此时如果需要使用无参构造器,必须手动显式定义;
- 构造器的重载:一个类中可以定义多个参数列表不同的构造器,即构造器的重载,以满足不同的初始化需求。
构造器的使用示例:
public class StudentTest {
public static void main(String[] args) {
// 调用无参构造器创建对象
Student s1 = new Student();
s1.name = "播妞";
s1.age = 18;
// 调用有参构造器创建对象,创建时直接完成赋值
Student s2 = new Student("播仔", 19);
}
}三、this关键字
3.1 this的本质
this是Java中的一个关键字,本质是当前对象的引用。在成员方法中,this指向调用该方法的当前对象;在构造器中,this指向正在创建的对象。简单来说,哪个对象调用方法,this就指向哪个对象。
3.2 this的核心应用场景
this最主要的作用是解决成员变量与方法内局部变量的命名冲突问题。当方法内的局部变量与类的成员变量名称相同时,Java会默认采用就近原则访问局部变量,此时需要通过this.变量名的方式明确指定访问类的成员变量。
示例代码:
public class Student {
String name;
double score;
// 带参数的方法,参数名与成员变量名冲突
public void checkPass(double score) {
// this.score 访问成员变量,score 访问方法入参的局部变量
if (this.score >= score) {
System.out.println("成绩达标");
} else {
System.out.println("成绩未达标");
}
}
// 有参构造器中使用this
public Student(String name, double score) {
this.name = name;
this.score = score;
}
}四、封装特性
4.1 封装的核心定义
封装是面向对象编程的三大核心特性之一(封装、继承、多态)。其核心思想是:在设计类时,将事物的属性数据与处理数据的行为方法封装到同一个类中,对外部隐藏内部的实现细节,仅暴露必要的访问与操作入口。
4.2 封装的设计原则
封装的核心设计原则是合理隐藏、合理暴露:
- 合理隐藏:对类内部的属性数据进行隐藏,避免外部直接随意修改,保证数据的安全性;
- 合理暴露:对外提供统一的、受控的访问与操作入口,通过方法实现对数据的校验与处理。
在Java代码层面,通过访问修饰符实现隐藏与暴露的控制:
private(私有):被修饰的成员只能在当前类内部访问,实现成员的隐藏;public(公开):被修饰的成员可以在任意类中访问,实现成员的暴露。
4.3 封装的标准实现
规范的封装实现,通常会将类的成员变量用private修饰进行隐藏,然后为每个成员变量提供public修饰的getter(获取值)和setter(设置值)方法,实现对属性的受控访问。
示例代码:
public class Student {
// 私有成员变量,外部无法直接访问
private String name;
private double chinese;
private double math;
// 公开的getter/setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getChinese() {
return chinese;
}
public void setChinese(double chinese) {
// 可以在setter方法中添加数据校验逻辑
if (chinese >= 0 && chinese <= 100) {
this.chinese = chinese;
} else {
System.out.println("语文成绩输入不合法,必须在0-100之间");
}
}
public double getMath() {
return math;
}
public void setMath(double math) {
if (math >= 0 && math <= 100) {
this.math = math;
} else {
System.out.println("数学成绩输入不合法,必须在0-100之间");
}
}
// 成绩处理方法
public void printTotalScore() {
System.out.println(name + "同学的各科总分是:" + (chinese + math));
}
}通过封装,我们可以在setter方法中添加数据校验逻辑,避免非法数据的写入,保证了对象数据的安全性与完整性,这也是封装的核心价值之一。
五、JavaBean(实体类)规范
5.1 实体类的定义
JavaBean也称为实体类,是Java开发中一种符合特定规范的特殊类,其核心作用是对业务数据进行封装,仅负责数据的存取,是实现数据与业务逻辑分离的基础。
5.2 实体类的规范要求
一个标准的JavaBean实体类,必须满足以下两个核心要求:
- 类中的所有成员变量必须用
private修饰私有化,并为每个成员变量提供对应的public修饰的getter和setter方法; - 类中必须提供一个无参构造器,有参构造器为可选。
5.3 实体类的设计思想
实体类的设计遵循数据与业务处理相分离的原则:实体类的对象仅负责数据的存储与读取,而对数据的业务处理逻辑,交由专门的业务处理类来实现。这种设计方式可以让代码职责更加清晰,提升代码的可维护性与可扩展性。
标准实体类示例:
/**
* 用户实体类,仅负责用户数据的存取
*/
public class User {
// 私有成员变量
private Long id;
private String username;
private Integer age;
// 必须的无参构造器
public User() {
}
// 可选的有参构造器
public User(Long id, String username, Integer age) {
this.id = id;
this.username = username;
this.age = age;
}
// 所有成员变量的getter/setter方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}业务处理类示例:
/**
* 用户业务处理类,负责用户相关的业务逻辑
*/
public class UserOperator {
private User user;
public UserOperator(User user) {
this.user = user;
}
// 业务方法:判断用户是否成年
public boolean isAdult() {
return user.getAge() >= 18;
}
}六、static关键字
static关键字意为静态,可以用于修饰类的成员变量、成员方法,被修饰的成员称为静态成员,属于类本身,而非类的实例对象。
6.1 静态变量与实例变量
成员变量根据是否被static修饰,分为静态变量(类变量)和实例变量两类,二者的核心区别如下表:
| 特性 | 静态变量(类变量) | 实例变量(对象变量) |
|---|---|---|
| 修饰符 | 被static修饰 | 无static修饰 |
| 所属主体 | 属于类本身 | 属于每个实例对象 |
| 内存存储 | 内存中仅存在一份,与类一起加载 | 每个对象都有独立的一份,随对象创建 |
| 访问方式 | 推荐:类名.变量名;也可通过对象访问 | 仅能通过对象名.变量名访问 |
| 共享特性 | 被该类的所有实例对象共享 | 每个对象独有,互不影响 |
6.1.1 静态变量的使用示例
public class User {
// 静态变量:统计创建的用户对象数量,属于类,所有对象共享
public static int number;
// 实例变量:每个用户的用户名,属于每个对象
private String username;
public User() {
// 每创建一个对象,静态变量number自增1
User.number++;
}
// getter/setter省略
}public class UserTest {
public static void main(String[] args) {
// 类名直接访问静态变量,初始值为0
System.out.println(User.number);
// 创建3个用户对象
new User();
new User();
new User();
// 静态变量值变为3,被所有对象共享
System.out.println(User.number);
}
}执行结果:
0
36.1.2 静态变量的应用场景
当某个数据只需要一份,且需要被该类的所有实例对象共享访问、修改时,该数据就适合定义为静态变量。例如:系统配置项、计数器、全局常量等。
6.2 静态方法与实例方法
成员方法根据是否被static修饰,分为静态方法(类方法)和实例方法两类,二者的核心区别如下:
| 特性 | 静态方法(类方法) | 实例方法 |
|---|---|---|
| 修饰符 | 被static修饰 | 无static修饰 |
| 所属主体 | 属于类本身 | 属于每个实例对象 |
| 访问方式 | 推荐:类名.方法名();也可通过对象调用 | 仅能通过对象名.方法名()调用 |
| 访问限制 | 只能直接访问静态成员,不能直接访问实例成员和this关键字 | 可以直接访问静态成员和实例成员,可使用this |
6.2.1 静态方法的典型应用:工具类设计
静态方法最核心的应用场景是设计工具类。工具类是一种仅提供通用功能方法的类,其中的方法都是静态方法,每个方法完成一个通用的功能,供开发者直接调用,无需创建对象。
使用静态方法设计工具类的优势:
- 调用方便,直接通过类名即可调用方法,无需实例化对象;
- 节省内存,避免了创建大量仅用于调用方法的对象,减少了堆内存的开销。
工具类的设计规范:
- 工具类中的所有方法都定义为静态方法;
- 将工具类的构造器私有化,避免外部创建该类的实例对象。
标准工具类示例:
/**
* 字符串处理工具类
*/
public class StringUtils {
// 私有化构造器,禁止外部实例化
private StringUtils() {
}
/**
* 判断字符串是否为空
* @param str 待判断的字符串
* @return 为空返回true,否则返回false
*/
public static boolean isEmpty(String str) {
return str == null || str.trim().length() == 0;
}
/**
* 判断字符串是否为邮箱格式
* @param email 待判断的邮箱字符串
* @return 符合邮箱格式返回true,否则返回false
*/
public static boolean isEmail(String email) {
if (isEmpty(email)) {
return false;
}
String emailRegex = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";
return email.matches(emailRegex);
}
}工具类的使用示例:
public class UtilsTest {
public static void main(String[] args) {
// 直接通过类名调用静态方法
System.out.println(StringUtils.isEmpty(""));
System.out.println(StringUtils.isEmail("test@163.com"));
}
}6.3 static关键字的使用注意事项
- 静态方法中可以直接访问类中的静态成员,不可以直接访问实例成员;
- 实例方法中既可以直接访问静态成员,也可以直接访问实例成员;
- 实例方法中可以使用
this关键字,静态方法中不可以使用this关键字; - 在本类中访问静态成员时,可以省略类名不写;访问其他类的静态成员时,必须携带类名访问。
七、综合实践:电影信息展示系统
7.1 需求说明
- 展示系统中的全部电影基础信息(电影名称、票价);
- 支持用户根据电影编号(ID)查询电影的详细信息。
7.2 实现思路
- 设计电影实体类
Movie,封装电影的相关属性,符合JavaBean规范; - 设计电影业务处理类
MovieSystem,实现电影列表展示、根据ID查询电影详情的业务逻辑; - 编写测试主类,完成功能的测试验证。
7.3 代码实现
7.3.1 电影实体类
/**
* 电影实体类,封装电影数据
*/
public class Movie {
// 电影属性
private int id; // 电影编号
private String name; // 电影名称
private double price; // 电影票价
private double score; // 电影评分
private String director; // 导演
private String actor; // 主演
private String description; // 电影简介
// 无参构造器
public Movie() {
}
// 有参构造器
public Movie(int id, String name, double price, double score, String director, String actor, String description) {
this.id = id;
this.name = name;
this.price = price;
this.score = score;
this.director = director;
this.actor = actor;
this.description = description;
}
// getter/setter方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String getDirector() {
return director;
}
public void setDirector(String director) {
this.director = director;
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}7.3.2 电影系统业务处理类
/**
* 电影信息系统业务处理类
*/
public class MovieSystem {
// 存储电影数据的数组
private Movie[] movies;
// 初始化电影数据
public MovieSystem() {
movies = new Movie[4];
movies[0] = new Movie(1, "长津湖之水门桥", 45.0, 9.6, "徐克", "吴京、易烊千玺", "抗美援朝战争题材电影");
movies[1] = new Movie(2, "一点就到家", 38.0, 8.7, "许宏宇", "刘昊然、彭昱畅", "农村电商创业题材电影");
movies[2] = new Movie(3, "月球陨落", 52.0, 7.9, "罗兰·艾默里奇", "哈莉·贝瑞", "科幻灾难题材电影");
movies[3] = new Movie(4, "出拳吧,妈妈", 35.0, 7.9, "唐晓白", "谭卓、田雨", "女性励志题材电影");
}
/**
* 展示全部电影的基础信息
*/
public void showAllMovies() {
System.out.println("===== 系统全部电影信息 =====");
for (Movie movie : movies) {
System.out.println("电影编号:" + movie.getId() +
" | 电影名称:" + movie.getName() +
" | 电影票价:" + movie.getPrice() + "元");
}
System.out.println("============================");
}
/**
* 根据电影编号查询电影详情
* @param id 电影编号
* @return 查到的电影对象,未查到返回null
*/
public Movie queryMovieById(int id) {
for (Movie movie : movies) {
if (movie.getId() == id) {
return movie;
}
}
return null;
}
}7.3.3 测试主类
public class MovieTest {
public static void main(String[] args) {
// 初始化电影系统
MovieSystem movieSystem = new MovieSystem();
// 1. 展示全部电影基础信息
movieSystem.showAllMovies();
// 2. 根据ID查询电影详情
int queryId = 2;
Movie movie = movieSystem.queryMovieById(queryId);
if (movie != null) {
System.out.println("\n===== 电影详情信息 =====");
System.out.println("电影编号:" + movie.getId());
System.out.println("电影名称:" + movie.getName());
System.out.println("电影评分:" + movie.getScore() + "分");
System.out.println("电影票价:" + movie.getPrice() + "元");
System.out.println("导演:" + movie.getDirector());
System.out.println("主演:" + movie.getActor());
System.out.println("电影简介:" + movie.getDescription());
System.out.println("=========================");
} else {
System.out.println("未查询到编号为" + queryId + "的电影");
}
}
}7.4 执行结果
===== 系统全部电影信息 =====
电影编号:1 | 电影名称:长津湖之水门桥 | 电影票价:45.0元
电影编号:2 | 电影名称:一点就到家 | 电影票价:38.0元
电影编号:3 | 电影名称:月球陨落 | 电影票价:52.0元
电影编号:4 | 电影名称:出拳吧,妈妈 | 电影票价:35.0元
============================
===== 电影详情信息 =====
电影编号:2
电影名称:一点就到家
电影评分:8.7分
电影票价:38.0元
导演:许宏宇
主演:刘昊然、彭昱畅
电影简介:农村电商创业题材电影
=========================