什么是面向对象呢?
好比你正在用的抖音,开发者关注的是背后的逻辑代码,如何执行,如何给用户体验,如何加入数据,等等等。叫做面向过程
你作为一个用户在使用抖音时候只用关心如何使用软件就行,简化了过程,这就叫面向对象。
什么是类?
2. 类是干嘛的?
类只干一件事:定义一类事物拥有什么特征、能做什么。
- 特征:name(名字)、age(年龄)、color(颜色)
- 行为:eat()(吃饭)
类本身不是真实的狗,它只是狗的模板。
3. 对象是什么?
根据类模板造出来的具体东西,就叫对象(实例)。
java
运行
Dog dog = new Dog(); // 造了一只狗
Dog dog3 = new Dog("哈哈"); // 又造了一只狗
- 类 = 狗的模板
- 对象 = 模板造出来的一只只真实的狗
4. 最通俗的比喻
- 类 = 月饼模具
- 对象 = 用模具压出来的一个个月饼
模具(类)决定了月饼长什么样、有什么馅; 真实的月饼(对象)才是你能吃的东西。
5. 类的 3 个核心组成
class为定义类的关键字,ClassName为类的名字,{}中为类的主体。
如何定义一个类?
修饰符 class 类名 {
// 1. 成员变量(特征)
// 2. 构造方法(创建对象用)
// 3. 成员方法(行为)
}

类的成员变量可以赋值也可以不赋值。
class Dog {
String name;
int age;
String color;
class Dog {
// 1. 成员变量(特征)
String name;
int age;
String color = "黄色";
赋值之后就变成默认值,不然不赋值要么默认值
类名必须大驼峰 Dog、Person、Student(首字母大写)
类里包含三样东西
- 成员变量(特征)
- 构造方法(创建对象)
- 成员方法(行为)
先定义类,再创建对象
Dog dog = new Dog("旺财");
定义一个人类 Person
class Person {
String name;
int age;
public Person() { }
public void sleep() {
System.out.println(name + " 在睡觉");
}
}
类中包含的内容称为类的成员。属性主要是⽤来描述类的,称之为类的成员属性或者类成员变量。⽅ 法主要说明类具有哪些功能,称为类的成员⽅法。
那么方法怎么定义呢?
方法分为有参数的方法和无参数的方法。
. 无参方法:()里面空空啥也不写
格式:返回值 方法名(){}
java
运行
//无参:括号空
public void eat(){
System.out.println("小狗吃饭");
}
功能:固定逻辑,不需要外部给数据,吃饭内容写死
2. 有参方法:()里必须写「数据类型 + 参数名」(形参)
格式:返回值 方法名(类型 变量){}
java
运行
//有参:括号内写类型+变量name
public void eat(String dogName){
System.out.println(dogName+"吃饭");
}
功能:需要外部传入数据,名字由调用时传入
二、调用区分(使用方法时,最容易错在这里)
1. 无参调用:括号必须空,不能填任何值
java
运行
Dog d = new Dog();
d.eat(); //正确:空括号
//d.eat("旺财"); 错误!无参不能传数据
2. 有参调用:括号必须传对应类型的数据(实参),个数、类型一一对应
java
运行
d.eat("旺财");//正确:传入字符串
//d.eat(); 错误!有参方法不能为空括号
三、一句话通俗理解
- 无参方法:自带全部数据,自己就能干活,不用你给东西 例:固定输出小狗吃饭
- 有参方法:缺数据干不了活,调用必须你给它数据 例:不知道狗叫啥,需要你传入名字
四、构造方法同理(你之前学的 Dog 构造)
java
运行
public Dog(){}//无参构造 → new Dog();
public Dog(String n){}//有参构造 → new Dog("旺财");
五、避错 3 条铁律(杜绝乱调用)

注意事项: 1. ⼀般⼀个⽂件当中只定义⼀个类 2. public修饰的类必须要和⽂件名相同 3. 不要轻易去修改public修饰的类的名称,如果要修改,通过开发⼯具修改、
类的实现
二、完整示例
java
运行
//1.定义类(模板)
class PetDog{
String name;
String color;
//成员方法
void barks(){
System.out.println(name+":旺旺旺~~~");
}
void wag(){
System.out.println(name+":摇尾巴~~~");
}
}
public class Main {
public static void main(String[] args) {
//2.实例化:new创建对象
PetDog dogh = new PetDog();
PetDog dogs = new PetDog();
//3.对象.变量 赋值
dogh.name = "阿黄";
dogh.color = "黑色";
//对象.方法() 调用
dogh.barks();
dogh.wag();
dogs.name = "赛虎";
dogs.color = "黄色";
dogs.barks();
dogs.wag();
}
}
三、运行结果
plaintext
阿黄:旺旺旺~~~
阿黄:摇尾巴~~~
赛虎:旺旺旺~~~
赛虎:摇尾巴~~~
四、口诀
先写类做模板,new 实例造对象,小数点来访问,多个对象各存值。new关键字⽤于创建⼀个对象的实例. • 使⽤. 来访问对象中的属性和⽅法. • 同⼀个类可以创建多个实例. 3.3 类和对象的再次理解
一、this 是什么?
this = 当前对象自己 谁在调用方法 / 构造方法,this 就代表谁。
你可以把 this 理解成:“我自己”
二、this 最核心 3 个用法(必考、必用、必须会)
1. this. 成员变量 → 区分 “局部变量” 和 “成员变量”
最常见场景:构造方法里参数名和成员变量重名!
java
运行
class Dog {
// 成员变量
String name;
// 构造方法
public Dog(String name) {
// 等号左边:this.name = 成员变量
// 等号右边:name = 传入的参数
this.name = name;
}
}
不加 this 会出错:
java
运行
name = name; // 自己给自己赋值,成员变量没被赋值!
加 this 才正确:
java
运行
this.name = name; // 我的名字 = 你传进来的名字
2. this () → 调用本类的其他构造方法
作用:构造方法之间互相调用,减少重复代码
java
运行
class Dog {
String name;
// 无参构造
public Dog() {
// 调用下面的有参构造
this("无名小狗");
}
// 有参构造
public Dog(String name) {
this.name = name;
}
}
规则:
- this() 必须在构造方法第一行
- 只能在构造方法里用
- 不能自己调用自己(会死循环)
3. this. 成员方法 → 调用本类的方法
java
运行
public void eat() { … }
public void play() {
// 调用自己的eat方法
this.eat();
}
平时可以省略,写 eat(); 就行。
三、最清晰的一句话总结
this 就是 “当前对象自己”
- this.变量 → 用自己的成员变量
- this() → 调用自己的其他构造
- this.方法() → 调用自己的方法
四、超级好记的例子(你一看就会)
java
运行
class Dog {
String name;
// 构造1:无参
public Dog() {
// 调用构造2
this("小狗");
}
// 构造2:有参
public Dog(String name) {
// 成员变量 = 参数
this.name = name;
}
public void show() {
// 调用自己的方法
this.printName();
}
public void printName() {
System.out.println("我叫:" + this.name);
}
}
五、this 必背 3 条铁律
最终总结
一句话:
this. 就是用来区分:哪个是对象自己的变量,哪个是参数!
二、作用 2:调用本类的其他构造方法(代码复用)
场景:
一个类有多个构造方法(无参、有参),想让它们互相调用。
java
运行
class Dog {
String name;
// 无参构造
public Dog() {
// 调用下面的有参构造
this("无名小狗");
}
// 有参构造
public Dog(String name) {
this.name = name;
}
}
规则(必须背):
三、最终极简总结(构造方法里的 this)
四、最容易记的比喻
一句话终极记忆
构造方法里: this. 赋值,this () 调用别的构造!
需要我给你出1 道改错题,马上检验你是否真的懂了吗?
ok
1 道必懂改错题(专门考 this 在构造方法里的用法)
题目
下面代码有 2 个错误,运行后名字不会变成 “旺财”,请找出并改正!
java
运行
class Dog {
String name;
// 构造方法
public Dog(String name) {
name = name; // 错误1
}
public Dog() {
// 想调用有参构造,默认名字"小狗"
this("小狗");
System.out.println("无参构造"); // 错误2
}
}
你的任务
直接把你的答案发我,我马上给你批改 + 详细讲解!
this.name=name;
太棒了!第一个错误你完全答对了! 👏
错误 1 修正(你答对了)
错误原因: name = name; 只是把参数自己赋值给自己,对象的成员变量根本没收到值。
正确写法:
java
运行
public Dog(String name) {
this.name = name; // ✅ 你写对了!
}
现在来看 错误 2(必考重点)
java
运行
public Dog() {
this("小狗"); // 调用构造
System.out.println("无参构造"); // 这行代码放这里对吗?
}
问题: this(…) 调用其他构造方法时,必须放在构造方法的第一行!
这里虽然逻辑没错,但我要考你的是顺序。 如果代码写成这样:
java
运行
public Dog() {
System.out.println("无参构造"); // 先写了别的代码
this("小狗"); // 后写 this() —— ❌ 报错!
}
这才是真正的错误!
最终终极结论(背下来,构造方法 this 永不丢分)
最终正确完整代码
java
运行
class Dog {
String name;
// 有参构造
public Dog(String name) {
this.name = name; // ✅ this. 赋值
}
// 无参构造
public Dog() {
this("小狗"); // ✅ this() 调用,必须第一行
System.out.println("无参构造");
}
}
- this = 当前对象自己
- this. 变量 → 解决变量重名
- this() → 调用本类构造
- this. 方法 () → 调用本类方法
-
this 关键字在构造方法里的 2 个核心作用(超级直白解释)
我只讲构造方法里的用法,专门帮你搞定你现在最晕的点!
一、作用 1:给成员变量赋值(最常用、最重要)
场景:
构造方法的参数名字 和 类里的成员变量名字 一模一样!
java
运行
class Dog {
String name; // 成员变量// 构造方法
public Dog(String name) { // 参数 name
// 这里必须用 this!
this.name = name;
}
}为什么要用 this?
- name = name; → 错! 自己给自己赋值,成员变量根本没拿到值
- this.name = name; → 对!
- this.name = 我这个对象自己的名字(成员变量)
- name = 你传进来的参数
- this() 只能写在构造方法里面
- 必须放在第一行!
- 作用:少写重复代码
- this.变量 → 给当前对象的成员变量赋值(解决名字重复)
- this(…) → 调用本类的其他构造方法(简化代码)
- this = 我自己(当前正在创建的对象)
- this.name = 我的名字
- this("旺财") = 调用我自己的另一个构造方法
- 指出错误 1是什么?怎么改?
- 指出错误 2是什么?怎么改?
- 赋值用 this.变量
java
运行
this.name = name;
- 调用构造用 this(…)
java
运行
this("参数");
- this(…) 必须写在第一行!

- 当自己给自己赋值时就会出现全部为0;没有给对象赋值


当只调用d1的setday和printdate时
当d1调用时this对setday进行标识为d1setday;

参数的实列化;
初始化

当变量没有赋值的时候,根据类型的不同进行默认初始化。
一、按变量位置分:成员变量 vs 局部变量
这是最基础的分类,先分清变量写在哪:
1. 成员变量初始化(3 种方式)
成员变量属于对象 / 类,系统会自动赋默认值,也可以手动初始化。
(1)默认初始化(系统自动执行,无需写代码)
创建对象时,JVM 自动给成员变量赋默认值,不用手动赋值。
- 基本类型:int=0、double=0.0、boolean=false、char=\\u0000
- 引用类型:null(对象、数组、String 等)
public class Student {
// 成员变量,系统自动初始化
int age; // 默认 0
String name; // 默认 null
boolean isMale; // 默认 false
public static void main(String[] args) {
Student s = new Student();
System.out.println(s.age); // 输出 0
System.out.println(s.name); // 输出 null
}
}
(2)显式初始化(声明时直接赋值)
定义变量时直接写值,优先级高于默认初始化。
public class Student {
// 显式初始化
int age = 18;
String name = "张三";
public static void main(String[] args) {
Student s = new Student();
System.out.println(s.age); // 输出 18
}
}
(3)构造器初始化(创建对象时赋值)
用构造方法给成员变量赋值,灵活性最高,适合创建对象时动态传值。
构造方法不能给返回类型!
public class Student {
int age;
String name;
// 构造器初始化
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public static void main(String[] args) {
// 创建对象时赋值
Student s = new Student(20, "李四");
System.out.println(s.age); // 输出 20
}
}
2. 局部变量初始化(强制手动赋值)
局部变量属于方法 / 代码块,JVM 不会自动赋默认值,必须手动赋值后才能使用,否则编译报错!
public class Test {
public void method() {
// 局部变量
int num;
// 错误!未赋值不能使用
// System.out.println(num);
num = 10; // 手动赋值
System.out.println(num); // 正确,输出 10
}
}
二、按静态 / 非静态分:静态初始化
针对静态变量(static 修饰),属于类本身,和对象无关,加载类时就执行。
1. 静态变量初始化(2 种方式)
(1)静态显式初始化
声明静态变量时直接赋值:
public class Test {
// 静态显式初始化
static int count = 100;
}
(2)静态代码块初始化
用 static{} 代码块,适合复杂逻辑初始化(比如赋值前计算、读取配置),类加载时执行 1 次。
public class Test {
static int num;
// 静态代码块:类加载时执行,仅执行一次
static {
num = 200;
System.out.println("静态代码块执行");
}
public static void main(String[] args) {
System.out.println(num); // 输出 200
}
}
三、补充:实例代码块初始化
属于对象初始化,用 {} 写在类里、方法外,创建对象时自动执行,每次 new 对象都会跑一次。
执行时机:早于构造器,晚于显式初始化。
public class Test {
int a = 10; // 显式初始化
// 实例代码块
{
a = 20;
System.out.println("实例代码块执行");
}
// 构造器
public Test() {
a = 30;
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.a); // 输出 30
}
}
四、Java 初始化执行顺序(必考!)
把所有初始化方式放一起,固定执行顺序:
口诀:先静态,后对象;先默认,后手动;先代码块,后构造器
总结
构造方法初始化
一、什么是构造方法?
构造方法 = 创建对象时自动调用的 “特殊方法” 作用:给对象初始化赋值(给成员变量设初值)。
你可以把它理解成:
new 对象的时候,第一个自动跑的方法
二、构造方法的 5 个核心特点(必须背下来)
三、最简单示例
java
运行
public class Student {
// 成员变量
String name;
int age;
// 构造方法:名字 = 类名,无返回值
public Student() {
name = "默认名字";
age = 18;
System.out.println("构造方法执行了!");
}
public static void main(String[] args) {
// new 对象 → 自动调用构造方法
Student s = new Student();
System.out.println(s.name); // 输出:默认名字
}
}
运行结果:
plaintext
构造方法执行了!
默认名字
四、带参数的构造方法(最常用!)
用来创建对象时直接赋值,非常方便。
java
运行
public class Student {
String name;
int age;
// 带参构造
public Student(String name, int age) {
this.name = name; // this 代表当前对象
this.age = age;
}
public static void main(String[] args) {
// 创建对象时直接传值
Student s1 = new Student("张三", 20);
Student s2 = new Student("李四", 22);
System.out.println(s1.name); // 张三
System.out.println(s2.name); // 李四
}
}
重点:this 是什么?
this.变量名 = 当前对象的成员变量 用来区分成员变量和方法参数重名的情况。
五、构造方法重载
一个类可以有多个构造方法,参数不同就行。
java
运行
public class Student {
String name;
int age;
// 无参构造
public Student() {}
// 1参构造
public Student(String name) {
this.name = name;
}
// 2参构造
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
六、超级重要规则(面试 / 写代码必坑)
规则 1:如果你写了带参构造
Java 不会再给你自动生成无参构造!
这会导致:
java
运行
Student s = new Student(); // 报错!找不到无参构造
解决办法
手动写一个无参构造方法(企业开发规范)。
七、构造方法执行顺序(和初始化一起考)
八、一句话总结构造方法
这就是: 无参构造 → 调用 → 三参构造 → 完成初始化
代码原理一步步拆解
看你给的代码:
java
运行
public Date(){
this(1900, 1, 1); // ① 这一行就是“跳转指令”
}
public Date(int year, int month, int day) { // ② 跳到这里执行
this.year = year;
this.month = month;
this.day = day;
}
执行流程(超级重要)
当你写:
java
运行
Date d = new Date();
真正发生的是:
为什么必须写在第一行?
Java 规定: 对象必须先完成核心初始化,才能执行你自己的代码。
所以:
比如:
java
运行
Date() {
this(1,2,3);
}
Date(int y,int m,int d) {
this();
}
这就变成: 无参 → 三参 → 无参 → 三参 → 死循环! Java 直接编译报错,不让你这么写。
最终极通俗总结
this(…) 的原理就是:
当前构造方法,委托另一个构造方法来完成对象初始化。
作用:

一、构造里 this (参数) 调用本类其他构造
1. 作用:代码复用
无参构造不想重复写赋值代码,直接调用三参构造。
java
运行
public Date(){
this(1900,1,1); // 调用下面三个参数的构造
}
new Date() → 走无参构造 → this(1900,1,1) 跳转执行 Date(int y,int m,int d),自动给年月日赋值 1900-1-1。
两条硬性规矩
构造方法里用 this(…) 调用别的构造,原理到底是什么?
一句话核心原理
你在一个构造方法里写 this(…),本质就是:让当前对象先去执行另一个构造方法,再回来继续执行自己。
用生活例子秒懂
你去餐厅吃饭:
一、执行流程图(new Date () 全过程)
plaintext
new Date()
↓
开辟堆内存 → 成员变量默认初始化(year=0,month=0,day=0)
↓
进入【无参构造 Date()】
↓
第一行:this(1900,1,1) → 跳转【三参构造Date(int y,m,d)】
↓
三参内:this.year=1900;this.month=1;this.day=1(覆盖默认值)
↓
三参构造执行结束,回到无参构造剩余代码(无代码直接结束)
↓
对象创建完毕,地址赋给引用变量
二、为什么必须第一行?
对象要先完成属性初始化,才能执行构造里自定义代码。 如果:
java
运行
public Date(){
System.out.println("123");
this(1900,1,1);
}
先执行输出再初始化,违背 Java 对象创建顺序 → 编译报错。
三、循环调用报错原理图
plaintext
无参构造() → this(1,2,3)→三参构造
↓
三参构造→this()→无参构造
↓
无限递归闭环 → 编译器直接禁止
四、核心原理一句话
this(实参) = 本构造把初始化工作委托给同类另一个构造,共用一套赋值逻辑,消除重复代码。
小练习
java
运行
public Test(){
this(5);
System.out.println("A");
}
public Test(int a){
System.out.println("B");
}
new Test();
为什么不能循环调用?
java
运行
// 报错
public Date(){
System.out.println();
this(1900,1,1);
}
java
运行
Date(){this(1900,1,1);}
Date(int a,int b,int c){this();}
无参调三参、三参又调无参,死循环,编译报错。
修饰符补充
构造一般 public,全项目随便 new; private 修饰构造:外面不能 new 对象,单例模式专用。
二、打印对象:toString ()
1. 不重写 toString,直接打印对象
java
运行
Person p = new Person("Jim","男",18);
System.out.println(p);
输出:包名.Person@一串哈希码 原因:所有类默认继承 Object,自带原生 toString,只输出地址格式,不输出属性。
2. 重写 toString:自定义打印内容
java
运行
@Override
public String toString() {
return "[" + name + "," + gender + "," + age + "]";
}
System.out.println(对象)时,自动调用 toString 方法,打印你 return 的字符串。 输出:[Jim,男,18]
一句话总结
一、封装到底是什么?
一句话: 把类的成员变量(数据)私有化,不让外面随便改,只留公开的方法给外面访问和修改。
就像:
- 你的书包(类)
- 里面的东西(成员变量)
- 你不想别人随便乱翻、乱改
- 只允许别人通过你允许的方式看 / 拿(get/set 方法)
这就是 封装。
二、为什么要用封装?
三、封装的 3 个固定步骤(背会这 3 步就会了)
步骤 1:给成员变量加 private(私有化)
java
运行
private String name;
private int age;
加了 private → 只有自己类能访问,外面不能直接访问。
步骤 2:提供公开的 get 方法(获取值)
java
运行
public String getName() {
return name;
}
public int getAge() {
return age;
}
步骤 3:提供公开的 set 方法(设置值)
java
运行
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
四、完整封装示例(你能直接看懂)
java
运行
public class Student {
// 1. 私有化成员变量(封装核心)
private String name;
private int age;
// 构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 2. get 方法:获取值
public String getName() {
return name;
}
public int getAge() {
return age;
}
// 3. set 方法:修改值
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
// 封装最大好处:可以加判断!安全!
if(age < 0 || age > 150) {
System.out.println("年龄不合法!");
return;
}
this.age = age;
}
}
使用的时候:
java
运行
public class Test {
public static void main(String[] args) {
Student s = new Student("张三", 18);
// 不能直接 s.name 或 s.age(会报错)
// 必须用 get/set
System.out.println(s.getName());
System.out.println(s.getAge());
s.setName("李四");
s.setAge(20);
s.setAge(1000); // 不合法,会被拦截
}
}
五、封装的最大好处:可以加校验规则
没封装前:
java
运行
s.age = 1000; // 能改,但不合理
有封装后:
java
运行
public void setAge(int age) {
if(age < 0 || age > 150) {
System.out.println("年龄错误!");
return;
}
this.age = age;
}
数据变得安全、干净、合法!
六、最简单总结(必背)
你之前的代码,改成封装版就是这样:
java
运行
public class Student {
// 私有化
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// get、set
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; }
}
你现在完全懂封装了吗? 我可以再给你做一个不封装 vs 封装的对比,让你一眼看出差距!
好的
封装终极对比:不封装 vs 封装
我用最直观的代码对比,让你一眼看懂封装到底有多重要!
一、先看:不封装的代码(危险版)
java
运行
public class Student {
// 没有private,谁都能改!
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
测试(随便乱改,完全失控)
java
运行
public class Test {
public static void main(String[] args) {
Student s = new Student("张三", 18);
// 想怎么改就怎么改,没有任何限制!
s.age = -10; // 年龄负数,不合理
s.age = 1000; // 年龄1000岁,荒唐
s.name = ""; // 名字为空
System.out.println(s.age); // 输出 1000
}
}
问题:
数据完全不安全,想改就改,程序会出大问题!
二、再看:封装后的代码(安全版)
java
运行
public class Student {
// 1. 私有化(核心!)
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 2. get方法:获取数据
public String getName() {
return name;
}
public int getAge() {
return age;
}
// 3. set方法:设置数据(能加判断!)
public void setAge(int age) {
// 封装的超级好处:拦截非法数据!
if(age < 0 || age > 150) {
System.out.println("年龄不合法!设置失败");
return;
}
this.age = age;
}
}
测试(非法数据会被拦住)
java
运行
public class Test {
public static void main(String[] args) {
Student s = new Student("张三", 18);
// s.age = 1000; ❌ 报错!不能直接访问private变量
s.setAge(1000); // 输出:年龄不合法!设置失败
s.setAge(20); // 正常
System.out.println(s.getAge()); // 输出 20
}
}
三、一句话总结差距
- 不封装:变量裸奔,谁都能改,数据不安全
- 封装:变量私有化,只能通过get/set访问,可控制、可校验、更安全
四、封装 3 句口诀(背会就精通)
java
运行
//包1
public class Person{
protected int age;
}
//包2,子类继承Person
public class Student extends Person{
void test(){
System.out.println(age);//跨包子类,protected可以访问
}
}
//包2,无关类
public class Test{
void test(){
Person p=new Person();
p.age;//报错!不同包非子类不能访问protected
}
}
4.public(公共)
规则:全范围无障碍,任何包、任何类都能访问
java
运行
//包A
public class A{
public String info="公共数据";
}
//任意包B
class B{
void fun(){
A a=new A();
System.out.println(a.info);//随便访问
}
}
快速记忆口诀
实际开发用法
把你之前 Student 改成标准封装版
java
运行
public class Student {
//1.私有化成员变量
private String name;
private int age;
private int food;
//构造
public Student(String name, int age) {
this.name = name;
//构造里也可以加校验
if(age>0&&age<150){
this.age = age;
}else{
this.age=0;
System.out.println("年龄非法");
}
}
//get取值
public String getName() {
return name;
}
public int getAge() {
return age;
}
//set改值(封装关键:拦截非法数据)
public void setAge(int age) {
if(age<0||age>150){
System.out.println("年龄设置失败!");
return;
}
this.age = age;
}
public void eat(String food) {
System.out.println(food+"马铃薯");
}
public void sleep() {
System.out.println(name + "在睡觉,现在" + age + "岁还睡觉呢?");
}
public static void main(String[] args) {
Student s = new Student("张三",18);
//s.age=200; 报错,private不能直接访问
s.setAge(200); //输出:年龄设置失败!
s.setAge(20);
System.out.println(s.getAge());//20
s.sleep();
}
}
需要我单独出一道封装练习题吗?
好的
封装课堂练习题(基础必做,跟着要求一步步写)
题目需求
创建 Person 类,完成标准封装:
先自己写,写完对照参考答案
参考答案
java
运行
public class Person {
//1.私有化成员变量
private String name;
private int age;
private char gender;
//无参构造
public Person(){}
//全参构造
public Person(String name, int age, char gender) {
this.name = name;
//构造器内同样做校验
setAge(age);
setGender(gender);
}
//get/set name
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//get/set age 带校验
public int getAge() {
return age;
}
public void setAge(int age) {
if(age < 0 || age > 120){
System.out.println("年龄不合法,赋值失败");
return;
}
this.age = age;
}
//get/set gender 带校验
public char getGender() {
return gender;
}
public void setGender(char gender) {
if(gender != '男' && gender != '女'){
System.out.println("性别错误");
return;
}
this.gender = gender;
}
//展示信息方法
public void showInfo(){
System.out.println("姓名:"+name+",年龄:"+age+",性别:"+gender);
}
public static void main(String[] args) {
//1.全参构造创建对象
Person p = new Person("小明",25,'男');
p.showInfo();
//2.修改年龄
p.setAge(-5);
p.setAge(200);
p.setAge(30);
//3.修改性别
p.setGender('x');
p.setGender('女');
p.showInfo();
}
}
运行结果
plaintext
姓名:小明,年龄:25,性别:男
年龄不合法,赋值失败
年龄不合法,赋值失败
性别错误
姓名:小明,年龄:30,性别:女
Java 4 种访问修饰符完整详解(对照表格)
表格
| private私有 | ✅能访问 | ❌不能 | ❌不能 | ❌不能 |
| default默认 (不写修饰符) | ✅能访问 | ✅能访问 | ❌不能 | ❌不能 |
| protected受保护 | ✅能访问 | ✅能访问 | ✅能访问 | ❌不能 |
| public公共 | ✅能访问 | ✅能访问 | ✅能访问 | ✅能访问 |
1.private(私有)
规则:只允许本类内部访问,出了这个类哪里都不能直接用
java
运行
class Student{
private String name;
//本类里可以正常用
public void test(){
System.out.println(name);
}
}
//同包别的类
class Test{
public static void main(String[] args){
Student s=new Student();
s.name;//报错!private出类不能访问
}
}
2.default(默认权限,不写任何关键字)
规则:同一个包内所有类随便访问,跨包一律不行
没有default关键字,变量 / 方法前面啥都不加就是默认权限
java
运行
//包A
class Student{
String name;//默认权限
}
//同包A的Test类
class Test{
void fun(){
Student s=new Student();
System.out.println(s.name);//正常访问
}
}
//包B(跨包)
class Demo{
void fun(){
Student s=new Student();
s.name;//编译报错,跨包不能访问default
}
}
3.protected(受保护)
规则:
包 1:com.one → Parent.java(父类,四种修饰变量)
java
运行
package com.one;
public class Parent {
//四种访问权限成员变量
private int pri = 10; //private私有
int def = 20; //default默认(不写修饰符)
protected int pro = 30; //protected受保护
public int pub = 40; //public公共
//本类内部:四种全能访问 ✅
public void show() {
System.out.println("本类访问:"+pri+" "+def+" "+pro+" "+pub);
}
}
包 1:com.one → TestSame.java(同包无关类)
java
运行
package com.one;
//和Parent同一个包
public class TestSame {
public static void main(String[] args) {
Parent p = new Parent();
//同包:private❌,default✅ protected✅ public✅
//System.out.println(p.pri); //报错,私有不能跨类
System.out.println("同包普通类:"+p.def+" "+p.pro+" "+p.pub);
}
}
运行结果:同包普通类:20 30 40
包 2:com.two → Son.java(跨包、继承 Parent)
java
运行
package com.two;
import com.one.Parent;
//跨包子类
public class Son extends Parent{
public void test(){
//不同包子类:private❌ default❌ protected✅ public✅
//System.out.println(pri); //报错
//System.out.println(def); //报错,default跨包不行
System.out.println("跨包子类:"+pro+" "+pub);
}
public static void main(String[] args) {
Son s = new Son();
s.test();
}
}
运行结果:跨包子类:30 40
包 2:com.two → TestOther.java(跨包、无关类,无继承)
java
运行
package com.two;
import com.one.Parent;
//不同包毫无关系
public class TestOther {
public static void main(String[] args) {
Parent p = new Parent();
//跨包无关:只有public✅,剩下全❌
//p.pri; p.def; p.pro; 全部编译报错
System.out.println("跨包无关类:"+p.pub);
}
}
运行结果:跨包无关类:40
汇总表格(对照代码)
表格
| private | ✅ | ❌ | ❌ | ❌ |
| default (无关键字) | ✅ | ✅ | ❌ | ❌ |
| protected | ✅ | ✅ | ✅ | ❌ |
| public | ✅ | ✅ | ✅ | ✅ |
回到【封装】核心总结
- setAge:年龄范围 0~120,超出提示「年龄不合法,赋值失败」,不修改值
- setGender:性别只能是 '男' / '女',其他字符提示「性别错误」
static
在Java中,被static修饰的成员,称之为静态成员,也可 以称为类成员,其不属于某个具体的对象,是所有对象所共享的。static修饰的成员变量,称为静态成员变量,静态成员变量最⼤的特性:不属于某个具体的对象,是所 有对象所共享的。
1. 不属于某个具体对象,是所有对象共享的
口头一点就是说黑板是大家都在看的(static类),但是每个人手上的平板电脑或者私人物品时不可看到的(非静态类·)
每个人·都有自己的名字·,但是教师是共用的。
2. 不存储在对象空间里,存在方法区
- 普通变量(name):存在 每个对象自己的空间
- 静态变量(classroom):存在 类的公共空间(方法区) 不管你创建 1 个对象 还是 100 个对象,静态变量只有 1 份!
- 3. 可以用对象访问,更推荐用类名访问
Student.classroom = "302教室";//类名访问推荐
Student s = new Student();
s.classroom = "302教室";//对象访问不推荐
静态变量 → 属于类,不属于对象 → 用类名访问!
4. 生命周期:随类加载而生,随类卸载而死
- 对象:new 的时候出生,不用了就被回收
- 静态变量: 程序一运行,类被加载 → 静态变量就出生了 程序结束,类被卸载 → 静态变量才消失
所以: 静态变量活得比对象久得多!
静态方法属于类,没有 this;非静态成员 / 方法属于对象,依赖 this,没对象就用不了,所以静态里不能直接调用非静态。
this 代表当前调用方法的对象,static 不用 new 对象就能执行,不存在 this。
示例代码
java
运行
public class Student {
//非静态成员变量(依附对象)
private String name = "张三";
//非静态方法,隐含this
public void show() {
System.out.println(this.name);
}
//静态方法,没有this
public static void testStatic() {
// 报错!静态不能直接访问非静态变量
// System.out.println(name);
// 报错!静态不能直接调用非静态方法
// show();
// 正确用法:手动创建对象,通过对象访问
Student s = new Student();
System.out.println(s.name);
s.show();
}
public static void main(String[] args) {
testStatic();
}
}
原因拆解
java
运行
//等价底层:public void show(Student this){}
调用时:s.show() → 自动把 s 传给 this。
反过来:非静态方法可以随便用静态
非静态自带 this(有对象),静态属于类,随时可取:
java
运行
public static int num = 100;
public void normalMethod(){
//正常运行
System.out.println(num);
testStatic();
}
口诀
静态不能直接碰实例,实例随便碰静态;缺对象缺 this,就是根本原因。

二、逐个讲 + 代码演示
1. 静态代码块(最特殊)
格式
java
运行
static {
// 代码
}
特点
- 类一加载就执行
- 整个程序中只执行 1 次!
- 用来:初始化静态变量、加载配置、只做一次的准备工作
例子
java
运行
public class Student {
static {
System.out.println("我是静态代码块,只执行一次!");
}
}
2. 构造块 / 实例代码块(常用)
格式
java
运行
{
// 代码
}
特点
- 每次 new 对象都会执行
- 执行时机:在构造方法之前!
- 用来:所有构造方法都要执行的公共代码
例子
java
运行
public class Student {
// 构造块
{
System.out.println("我是构造块,每次new对象都先执行我!");
}
// 构造方法
public Student() {
System.out.println("我是构造方法!");
}
}
3. 普通代码块(最简单)
格式
写在方法里面
java
运行
public void test(){
{
//普通代码块
}
}
特点
- 就是方法里的一段代码
- 调用方法才执行
- 用来限制变量作用域
三、终极执行顺序(面试必考!)
执行顺序:
静态代码块 → 构造块 → 构造方法 → 普通代码块
四、超级完整示例(直接运行看结果)
java
运行
public class Student {
// 1. 静态代码块(最优先,只执行1次)
static {
System.out.println("① 静态代码块执行");
}
// 2. 构造块(每次new都执行,在构造方法前)
{
System.out.println("② 构造块执行");
}
// 3. 构造方法
public Student() {
System.out.println("③ 构造方法执行");
}
// 普通方法 + 普通代码块
public void study() {
System.out.println("学习开始");
// 4. 普通代码块
{
System.out.println("④ 普通代码块执行");
}
System.out.println("学习结束");
}
public static void main(String[] args) {
System.out.println—–第一次创建对象—–");
Student s1 = new Student();
s1.study();
System.out.println—–第二次创建对象—–");
Student s2 = new Student();
}
}
运行结果(一定要看!)
plaintext
① 静态代码块执行(只来1次)
—–第一次创建对象—–
② 构造块执行
③ 构造方法执行
学习开始
④ 普通代码块执行
学习结束
—–第二次创建对象—–
② 构造块执行
③ 构造方法执行





