第一部分:封装(Encapsulation)
1. 封装的概念与作用
Q: 什么是封装?封装的核心思想是什么?
A: 封装是面向对象三大特征之一(封装、继承、多态),指将类的某些信息(属性)隐藏在类内部,不允许外部直接访问,而是通过该类提供的方法来访问。
核心思想:
- 隐藏:将敏感数据用
private修饰,外部无法直接访问 - 暴露:提供
public的getter/setter方法,控制数据的访问和修改 - 安全:在方法中进行数据校验,防止非法数据
// 未封装:数据暴露,不安全
public class StudentUnsafe {
String name;
int age; // 外部可以直接赋值,如 age = -20 也合法
}
// 已封装:数据隐藏,安全可控
public class Student {
private String name; // 私有,外部无法直接访问
private int age; // 私有
// 提供公共方法控制访问
public void setAge(int age) {
if (age < 0) {
System.out.println("年龄不能为负数!");
} else {
this.age = age; // 合法才赋值
}
}
public int getAge() {
return age;
}
}
2. private与public的区别
Q: private和public修饰符有什么区别?分别用于什么场景?
A:
| 修饰符 | 访问范围 | 使用场景 |
|---|---|---|
| public | 任意位置都可访问 | 类、方法、需要对外暴露的成员 |
| private | 只能在本类中访问 | 属性、内部辅助方法 |
public class AccessDemo {
// public:任意位置可访问
public String publicName = "公开姓名";
// private:只能在本类中访问
private int privateAge = 20;
private String privatePhone = "13800138000";
// public方法:对外提供访问通道
public void setAge(int age) {
if (age > 0 && age < 150) {
this.privateAge = age;
}
}
public int getAge() {
return privateAge;
}
public static void main(String[] args) {
AccessDemo demo = new AccessDemo();
// public成员:直接访问
System.out.println(demo.publicName); // 正常访问
// private成员:本类内可以访问
System.out.println(demo.privateAge); // 20,本类内可以
// 其他类中:private成员无法直接访问
// demo.privateAge = 30; // 在其他类中报错!
}
}
// 其他类
class OtherClass {
public void test() {
AccessDemo demo = new AccessDemo();
System.out.println(demo.publicName); // OK
// System.out.println(demo.privateAge); // 编译报错!
// 必须通过公共方法访问
demo.setAge(25);
System.out.println(demo.getAge()); // 25
}
}
3. 封装的开发步骤
Q: 实现封装的标准开发步骤是什么?
A: 三步走:
- 属性私有化:用
private修饰成员变量 - 提供setter方法:用于给属性赋值(可加入校验逻辑)
- 提供getter方法:用于获取属性值
public class Student {
// 步骤1:属性私有化
private String name;
private int age;
private double score;
// 步骤2:提供setter方法(赋值)
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
// 加入数据校验
if (age < 0 || age > 150) {
System.out.println("年龄不合法:" + age);
return;
}
this.age = age;
}
public void setScore(double score) {
if (score < 0 || score > 100) {
System.out.println("成绩不合法:" + score);
return;
}
this.score = score;
}
// 步骤3:提供getter方法(取值)
public String getName() {
return name;
}
public int getAge() {
return age;
}
public double getScore() {
return score;
}
}
// 使用
public class Test {
public static void main(String[] args) {
Student s = new Student();
// s.name = "张三"; // 报错!无法直接访问
s.setName("张三");
s.setAge(200); // 输出:年龄不合法:200
s.setAge(20); // 正常赋值
s.setScore(88.5);
System.out.println(s.getName() + "," + s.getAge() + "岁,成绩:" + s.getScore());
// 输出:张三,20岁,成绩:88.5
}
}
第二部分:this关键字
4. this关键字的作用
Q: this关键字是什么?主要解决什么问题?
A: this是一个引用变量,指向当前对象(调用该方法的对象)。
主要作用:
- 解决命名冲突:区分成员变量和局部变量
- 指代当前对象:在方法内部获取当前对象的引用
public class Student {
private String name; // 成员变量
private int age;
// 问题:参数名和成员变量名相同,如何区分?
public void setName(String name) { // 局部变量name
// name = name; // 错误!两个都是局部变量,自己赋值给自己
this.name = name; // 正确!this.name指成员变量,name指参数
}
public void setAge(int age) {
if (age < 0) {
System.out.println("年龄不合法:" + age);
} else {
this.age = age; // this.age是成员变量,age是参数
}
}
// this表示当前对象
public void printThis() {
System.out.println("当前对象:" + this); // 打印对象地址
}
}
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(s1); // Student@6d06d69c
s1.printThis(); // Student@6d06d69c(同一个对象)
Student s2 = new Student();
System.out.println(s2); // Student@7852e922
s2.printThis(); // Student@7852e922
// 结论:哪个对象调用方法,this就指向哪个对象
}
}
第三部分:构造器(Constructor)
5. 构造器的概念与特点
Q: 什么是构造器?有什么特点?
A: 构造器(构造方法)是一种特殊方法,用于创建对象时初始化对象。
特点:
| 特性 | 说明 |
|---|---|
| 名称 | 必须与类名完全相同 |
| 返回值 | 没有返回值,连 void都不能写 |
| 调用时机 | new对象时自动调用 |
| 默认构造器 | 不写构造器时,JVM自动生成无参构造器 |
| 重载 | 可以定义多个构造器(参数列表不同) |
public class Student {
private String name;
private int age;
// 无参构造器:创建对象,不初始化属性
public Student() {
System.out.println("无参构造器被调用");
}
// 有参构造器:创建对象,同时初始化属性
public Student(String name, int age) {
System.out.println("有参构造器被调用");
this.name = name;
this.age = age;
}
// 构造器重载:部分参数
public Student(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
Student s1 = new Student(); // 调用无参构造器
Student s2 = new Student("张三", 20); // 调用有参构造器
Student s3 = new Student("李四"); // 调用单参构造器
}
}
6. 构造器的注意事项
Q: 使用构造器有哪些重要注意事项?
A:
| 注意点 | 说明 |
|---|---|
| 一旦定义有参构造器,默认无参构造器消失 | 建议手动添加无参构造器 |
| 构造器可以调用其他构造器 | 使用 this(...),必须放在第一行 |
| 构造器不能递归调用 | 不能自己调用自己 |
public class Student {
private String name;
private int age;
// 注意:如果不写任何构造器,JVM会自动提供无参构造器
// 但如果写了有参构造器,JVM不再提供无参构造器!
// 建议:显式定义无参构造器
public Student() {
// this("未知", 0); // 可以调用其他构造器,必须放第一行
System.out.println("无参构造器");
}
// 有参构造器
public Student(String name, int age) {
// this(); // 调用无参构造器,必须放第一行
this.name = name;
this.age = age;
}
// 单参构造器:复用双参构造器
public Student(String name) {
this(name, 0); // 调用双参构造器,避免代码重复
}
}
public class Test {
public static void main(String[] args) {
// 常见错误:只定义有参构造器,却用无参方式创建对象
// Student s = new Student(); // 如果只有有参构造器,这行报错!
// 正确做法:始终保留无参构造器
Student s1 = new Student(); // OK
Student s2 = new Student("张三", 20); // OK
Student s3 = new Student("李四"); // OK,age默认为0
}
}
第四部分:JavaBean规范
7. JavaBean的标准规范
Q: 什么是JavaBean?需要满足哪些规范?
A: JavaBean(实体类)是一种符合特定规范的Java类,主要用于封装数据。
规范要求:
| 规范 | 说明 |
|---|---|
| 属性私有 | 所有成员变量用 private修饰 |
| 无参构造器 | 必须提供 public的无参构造器 |
| 全参构造器 | 建议提供(可选但推荐) |
| getter/setter | 为每个属性提供公共的访问方法 |
| toString() | 重写方法,方便打印对象信息 |
// 标准JavaBean示例
public class Student {
// 1. 属性私有
private String name;
private int age;
private double chinese;
private double math;
// 2. 无参构造器(必须)
public Student() {
}
// 3. 全参构造器(推荐)
public Student(String name, int age, double chinese, double math) {
this.name = name;
this.age = age;
this.chinese = chinese;
this.math = math;
}
// 4. getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getChinese() {
return chinese;
}
public void setChinese(double chinese) {
this.chinese = chinese;
}
public double getMath() {
return math;
}
public void setMath(double math) {
this.math = math;
}
// 5. 重写toString()
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", chinese=" + chinese +
", math=" + math +
'}';
}
}
// 使用
public class Test {
public static void main(String[] args) {
// 使用全参构造器快速创建对象
Student s = new Student("黑马", 20, 100, 90);
// 直接打印对象,显示属性值(因为重写了toString)
System.out.println(s);
// 输出:Student{name='黑马', age=20, chinese=100.0, math=90.0}
}
}
8. IDEA快速生成JavaBean
Q: IDEA中如何快速生成JavaBean的代码?
A: 快捷键:Alt + Insert(或右键 → Generate)
| 生成内容 | 操作步骤 |
|---|---|
| 构造器 | Alt + Insert → Constructor → 选择参数 |
| getter/setter | Alt + Insert → Getter and Setter → 全选属性 |
| toString() | Alt + Insert → toString() → 全选属性 |
public class Movie {
private String name;
private double score;
private String director;
// 1. 生成无参构造器:Alt+Insert → Constructor → Select None
public Movie() {
}
// 2. 生成全参构造器:Alt+Insert → Constructor → Ctrl+A全选
public Movie(String name, double score, String director) {
this.name = name;
this.score = score;
this.director = director;
}
// 3. 生成getter/setter:Alt+Insert → Getter and Setter → Ctrl+A全选
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// ... 其他getter/setter
// 4. 生成toString():Alt+Insert → toString() → Ctrl+A全选
@Override
public String toString() {
return "Movie{" +
"name='" + name + '\'' +
", score=" + score +
", director='" + director + '\'' +
'}';
}
}
第五部分:static关键字
9. static修饰成员变量
Q: static修饰的成员变量有什么特点?与实例变量有什么区别?
A: static变量(静态变量/类变量)属于类,在内存中只有一份,被所有对象共享。
| 特性 | 静态变量(static) | 实例变量(非static) |
|---|---|---|
| 归属 | 属于类 | 属于对象 |
| 内存份数 | 只有1份 | 每个对象1份 |
| 访问方式 | 类名.变量名(推荐)或 对象.变量名 |
对象.变量名 |
| 别名 | 类变量 | 实例变量 |
public class Student {
// 静态变量:属于类,只有一份,所有对象共享
public static String schoolName = "黑马程序员";
public static int count = 0; // 统计创建了多少个学生
// 实例变量:属于对象,每个对象独立
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
count++; // 每创建一个学生,计数+1
}
}
public class Test {
public static void main(String[] args) {
// 访问静态变量:类名.变量名(推荐)
System.out.println(Student.schoolName); // 黑马程序员
Student s1 = new Student("张三", 20);
Student s2 = new Student("李四", 21);
// 静态变量共享
System.out.println(Student.count); // 2(两个对象共享)
System.out.println(s1.count); // 2(也可以用对象访问,但不推荐)
System.out.println(s2.count); // 2
// 修改静态变量:影响所有对象
Student.schoolName = "传智教育";
System.out.println(s1.schoolName); // 传智教育
System.out.println(s2.schoolName); // 传智教育
}
}
10. static修饰成员方法
Q: 静态方法和实例方法有什么区别?如何选择?
A:
| 特性 | 静态方法(static) | 实例方法(非static) |
|---|---|---|
| 归属 | 属于类 | 属于对象 |
| 调用方式 | 类名.方法名()(推荐) |
对象.方法名() |
| 访问成员 | 只能访问静态成员 | 可以访问静态+实例成员 |
| this关键字 | 不能使用 | 可以使用 |
| 应用场景 | 工具方法、不依赖对象数据的方法 | 需要操作对象属性的方法 |
public class Student {
private String name; // 实例变量
private static int count = 0; // 静态变量
// 实例方法:需要操作对象属性
public void study() {
// 可以访问实例变量
System.out.println(name + "在学习");
// 也可以访问静态变量
System.out.println("当前学生数:" + count);
// 可以使用this
this.name = "学习中";
}
// 静态方法:不依赖对象,属于类
public static void printHello() {
System.out.println("Hello World");
// 不能访问实例变量!
// System.out.println(name); // 编译报错!
// 不能访问实例方法!
// study(); // 编译报错!
// 可以访问静态成员
System.out.println("学生总数:" + count);
// 不能使用this!
// this.name = "xxx"; // 编译报错!
// 如果必须访问实例成员,需要先创建对象
Student s = new Student();
s.name = "临时学生";
}
}
// 工具类:全部用静态方法
public class StringUtil {
// 构造器私有,防止创建对象
private StringUtil() {}
// 静态工具方法
public static int getLength(String str) {
return str.length();
}
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
}
// 使用
public class Test {
public static void main(String[] args) {
// 调用静态方法:类名.方法名
Student.printHello();
// 调用实例方法:必须先创建对象
Student s = new Student();
s.study();
// 工具类使用:无需创建对象
int len = StringUtil.getLength("hello"); // 5
boolean empty = StringUtil.isEmpty(""); // true
}
}
11. 静态代码块与实例代码块
Q: 什么是静态代码块和实例代码块?执行时机是什么?
A:
| 代码块类型 | 语法 | 执行时机 | 执行次数 |
|---|---|---|---|
| 静态代码块 | static { } |
类加载时执行 | 只执行1次 |
| 实例代码块 | { } |
创建对象时执行(构造器之前) | 每次创建对象都执行 |
public class Demo {
// 静态代码块:类加载时执行,只执行一次
static {
System.out.println("静态代码块执行");
// 常用于初始化静态变量
count = 100;
}
// 实例代码块:每次创建对象时执行,在构造器之前
{
System.out.println("实例代码块执行");
}
public static int count;
public Demo() {
System.out.println("构造器执行");
}
public static void main(String[] args) {
System.out.println("main开始");
Demo d1 = new Demo();
// 输出顺序:
// 静态代码块执行(类加载时)
// main开始
// 实例代码块执行
// 构造器执行
Demo d2 = new Demo();
// 输出:
// 实例代码块执行
// 构造器执行
// (静态代码块不再执行!)
}
}
第六部分:综合案例
12. 电影信息展示系统
Q: 如何设计一个电影信息展示系统?涉及哪些知识点?
A: 涉及知识点:
- JavaBean封装电影数据
- static静态变量存储共享数据
- static静态方法提供功能
- 数组存储多个对象
- 用户交互菜单
// 1. 电影实体类(JavaBean)
public class Movie {
private String name; // 电影名称
private double score; // 评分
private String director; // 导演
// 构造器、getter/setter、toString...
public Movie() {}
public Movie(String name, double score, String director) {
this.name = name;
this.score = score;
this.director = director;
}
// getter和setter...
public String getName() { return name; }
public double getScore() { return score; }
public String getDirector() { return director; }
}
// 2. 电影系统类(功能实现)
public class MovieSystem {
// 静态变量:存储所有电影(全局一份)
private static Movie[] movies;
// 静态代码块:初始化电影数据
static {
movies = new Movie[6];
movies[0] = new Movie("肖申克的救赎", 9.7, "弗兰克·德拉邦特");
movies[1] = new Movie("霸王别姬", 9.6, "陈凯歌");
movies[2] = new Movie("阿甘正传", 9.5, "罗伯特·泽米吉斯");
movies[3] = new Movie("泰坦尼克号", 9.5, "詹姆斯·卡梅隆");
movies[4] = new Movie("千与千寻", 9.4, "宫崎骏");
movies[5] = new Movie("黑马程序员", 9.9, "黑马");
}
// 静态方法:显示所有电影
public static void showAllMovies() {
System.out.println("=== 全部电影 ===");
for (int i = 0; i < movies.length; i++) {
System.out.println((i+1) + ". " + movies[i].getName());
}
}
// 静态方法:根据名称搜索电影
public static void showDetailByName(String name) {
int found = 0;
for (Movie movie : movies) {
// contains:模糊匹配
if (movie.getName().contains(name)) {
found++;
System.out.println("找到第" + found + "部:");
System.out.println(" 名称:" + movie.getName());
System.out.println(" 评分:" + movie.getScore());
System.out.println(" 导演:" + movie.getDirector());
}
}
if (found == 0) {
System.out.println("未找到相关电影");
}
}
}
// 3. 主程序(用户交互)
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("=== 黑马影院 ===");
System.out.println("1. 显示全部电影");
System.out.println("2. 搜索电影");
System.out.println("3. 退出");
System.out.print("请选择:");
int choice = sc.nextInt();
switch (choice) {
case 1 -> MovieSystem.showAllMovies(); // 调用静态方法
case 2 -> {
System.out.print("请输入电影名称:");
String name = sc.next();
MovieSystem.showDetailByName(name);
}
case 3 -> {
System.out.println("欢迎下次光临!");
return;
}
default -> System.out.println("无效选择");
}
}
}
}