Java 类与对象总结:从入门到“少踩坑”一篇就够(适用期末复习,学完一遍过重点)
Java 类与对象:从入门到“少踩坑”一篇就够
一、最本质:面向过程 vs 面向对象
以Java跟C为例,C++其实就是在C的基础上引入了面向对象的东西,与Java有很多相似之处
- 面向过程:
想解决问题,就把流程拆成一个个步骤,按顺序执行。代码像流水线:一步一步“做事情”。 - 面向对象:
不再围着“过程”打转,而是先找出有哪些对象,每个对象负责什么事,然后让对象们互相配合解决问题。
一句话版:
面向过程:亲自干活。
面向对象:先设计工人(对象),然后让工人干活。
二、类和对象:设计图 vs 房子
1. 类:一份“说明书”
在 Java 里,类(class)是对象的设计图,描述了某一类东西有什么属性、能做什么事:
class PetDog {
// 成员变量 = 属性
public String name;
public String color;
// 成员方法 = 行为
public void bark() {
System.out.println(name + " 汪汪叫");
}
}
关键点:
- 成员变量:描述对象“是什么”。
- 成员方法:描述对象“能做什么”。
2. 对象:真正“活起来”的东西
用 new 创建对象:
PetDog dog = new PetDog();
dog.name = "阿黄";
dog.color = "黑黄";
dog.bark();
要记住:
- 同一个类可以 new 出很多个对象,每个对象有自己的一份成员变量。
- 类本身不存储具体数据,只是模板;对象才真正占内存。
容易混淆的点
PetDog是类型,dog才是对象变量。- 多个对象之间的成员变量互不影响:改了
dog1.name不会影响dog2.name。
三、this:对象在方法里的“自我引用”
1. 为什么要有 this
典型写法:
public class Date {
public int year;
public int month;
public int day;
public void setDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
这里形参名和成员变量同名,用 this.year 明确指的是“当前对象的 year”。
如果你写成:
public void setDate(int year, int month, int day) {
year = year; // 完全没用
month = month; // 也是
day = day; // 还是
}
你只是在给形参自己赋值,成员变量完全没动,这是典型坑点,按照java的尿性,这几个输出全为0,根本没赋值。
2. this 的规则
this表示:当前正在执行这个方法的那个对象。- 只能出现在实例方法和构造方法中。
- 在静态方法里不能用
this(因为静态方法可能没有具体对象,没法指向谁)。
易错点
- 形参与成员变量同名却不加
this,导致赋值失效。 - 在
static方法里写this,直接编译报错。
四、构造方法:对象的“初始化入口”
1. 构造方法长什么样
public class Date {
public int year;
public int month;
public int day;
// 构造方法
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
System.out.println("构造 Date 对象");
}
}
特点:
- 方法名必须和类名一模一样。
- 没有返回值类型,连
void都不写。 new的时候自动调用,一个对象只会调用一次。
2. 构造方法重载 & this(…)
多种构造方式:
public class Date {
public int year = 1900;
public int month = 1;
public int day = 1;
public Date() {
// 使用默认值
}
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
也可以构造器之间互相调用:
public Date() {
this(1900, 1, 1); // 必须是第一句
}
3. 默认构造方法的“消失术”
- 如果你一个构造方法都不写,编译器会偷偷给你加一个无参构造。
- 一旦你手写了任何一个构造方法,这个默认无参构造就不再自动生成。
于是经典错误场景:
public class Person {
public Person(String name) {}
}
Person p = new Person(); // 编译失败:没有无参构造
4. 成员变量的默认值
- 成员变量:即使你没手动赋值,也会有默认值(int 是 0,boolean 是 false,引用是 null)。
- 局部变量:一定要自己赋值,否则编译不过。
构造相关易错点
- 写了有参构造却忘了手写无参构造,导致框架或测试里
new Class()挂掉。 - 在构造方法中调用
this(...),但不是第一句,编译错误。 - 构造方法互相
this(...)调用形成闭环,编译报“递归构造器调用”。
五、封装:把细节关在类里面
1. 思想
封装做的就是两件事:
- 把数据和操作数据的方法包在一起(写在同一个类里)。
- 对外只暴露必要接口,把实现细节藏起来。
典型写法:
public class Student {
private String name;
private int age;
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
}
}
public int getAge() {
return age;
}
}
外部不能直接乱改 age,必须通过 setAge,中间可以加校验。
2. 四个访问修饰符
按“开放程度”从高到低:
public:哪里都能访问。protected:同包 + 子类。- default(不写修饰符):同一个包内可以访问。
private:只能在本类内部访问。
另外:
- 顶级类(文件最外层那个类)只能是
public或 default。 - 一个
.java文件里最多有一个public class,文件名要和它同名。
封装相关易错点
- 把类写成
public之后把文件名忘记改成同名,编译报错。 - 成员是 default,不在同一个包中时访问失败 —— 包一换就炸。
- 字段暴露为
public,在项目里到处直接改,后期全是“谁改了这个值”的谜团。
六、包(package)与 import
简单记三条:
-
package com.demo.test;对应的物理路径就是com/demo/test/。 -
同一个工程里,只要包不同,就可以有同名类共存。
-
import是为了用短名字访问类:import java.util.Date; // 之后可以直接写 Date,而不用 java.util.Date
当有两个包里都有 Date(比如 java.util.Date 和 java.sql.Date)时:
- 同时
import java.util.*;和import java.sql.*;,再写Date就会“引用不明确”。 - 这时必须写全名:
java.util.Date或java.sql.Date。
七、static关键字:属于“类”,而不是属于某个对象
1. static 变量(类变量)
public class Student {
public String name;
public int age;
public static String classRoom = "Bit306";
}
特点:
- 只保存一份,所有对象共享。
- 更推荐用
Student.classRoom访问,而不是stu.classRoom。 - 随类加载创建,随类卸载销毁。
2. static 方法(类方法)
public class Student {
public String name;
public static String classRoom;
public static String getClassRoom() {
return classRoom; // OK
// return name; // 错误:不能访问非静态成员
}
}
规则:
- 调用不需要对象(直接类名+静态方法):
Student.getClassRoom()。 - 不能访问非静态成员变量 / 非静态方法。
- 不能使用
this。
原因很简单:静态方法可能在任何对象创建之前就被调用,根本不知道你想操作哪一个具体对象。
3. 静态代码块
public class Student {
public static String classRoom;
static {
classRoom = "Bit306";
System.out.println("加载 Student 类");
}
}
- 类第一次被使用时执行一次。并且只执行一次
- 用于复杂的 static 初始化比较方便。
static 易错点
- 在 static 方法里访问实例变量 / 实例方法,编译错误。
- 把明显属于“对象自己”的属性写成 static,导致所有对象共享数据,逻辑错得很隐蔽。
- 在构造函数里初始化 static 字段,造成语义混乱(虽然能跑,但不推荐)。
八、代码块:普通、实例、静态
1. 普通代码块
写在方法里的 { },就是个局部作用域:
public static void main(String[] args) {
{
int x = 10;
System.out.println(x);
}
int x = 100;
}
作用:变量作用范围控制。
2. 实例代码块(构造代码块)
写在类里、没有修饰符:
public class Student {
private String name;
private int age;
{ // 实例代码块
this.name = "默认名";
this.age = 18;
System.out.println("实例代码块执行");
}
public Student() {
System.out.println("构造方法执行");
}
}
特点:
- 每次 new 对象都会执行一次,而且早于构造方法。
- 可以把多个构造方法的公共初始化逻辑放这里(不过实际项目中更多是通过构造器之间
this()调用)。
3. 静态代码块(前面讲过)
- 类加载时执行一次。
- 用于初始化 static 成员。
执行顺序总览(记个大概就行)
- 类加载:
静态成员默认值 → 静态成员就地初始化 → 静态代码块(从上到下)。 - new 对象:
实例成员默认值 → 实例成员就地初始化 → 实例代码块 → 构造方法。
九、内部类:类里再套一个类
常见几种:
-
实例内部类
public class Outer { private int a; static int b; int c; class Inner { int c; public void test() { a = 10; // 访问外部类实例字段 b = 20; // 访问外部类静态字段 c = 30; // 内部类自己的 c Outer.this.c = 40; // 外部类的 c } } }创建方式:
Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();要点:必须先有外部类对象,才能 new 实例内部类。
-
静态内部类
public class Outer { static int b; static class Inner { public void test() { b = 20; // 只能访问外部类静态成员 } } } Outer.Inner inner = new Outer.Inner();不依赖外部类对象。
-
局部内部类 / 匿名内部类
- 局部内部类:写在方法里的类,只在方法内部使用。
- 匿名内部类:以后写接口实现、回调时到处能见,算是语法糖。
内部类是一个偏“语法增强”的工具,先记住有这个东西,知道基本使用方式就够了。
十、toString:打印对象时的“名片”
默认情况:
Person p = new Person();
System.out.println(p);
输出类似:com.demo.Person@1b6d3586,这只是对象地址的一个编码形式,看着没啥用。
常规做法是重写 toString():
public class Person {
String name;
String gender;
int age;
@Override
public String toString() {
return "[" + name + ", " + gender + ", " + age + "]";
}
}
以后直接 System.out.println(p); 就能打印友好的信息,非常利于调试、打日志。
十一、超浓缩易错点清单(复习时对着看一遍)
-
类与对象
- 类是模板,对象才占内存;多个对象各有自己的成员变量。
-
this
- 形参与成员变量同名时,一定要用
this.xxx = xxx;。 static方法里不能使用this。
- 形参与成员变量同名时,一定要用
-
构造方法
- 名字必须和类名一样,不能写返回值类型。
- 一旦自己写了构造方法,默认无参构造就没了;需要就手写。
this(...)调用其他构造器必须位于构造方法第一行。
-
变量初始化
- 成员变量有默认值,局部变量必须手动初始化。
-
封装与访问控制
- 字段通常用
private,暴露 getter/setter。 - 不同包访问 default 成员会失败;
public类文件名必须相同。
- 字段通常用
-
static
- static 变量共享一份数据;static 方法不能访问非 static 成员,也不能用
this。 - 静态代码块只在类加载时执行一次。
- static 变量共享一份数据;static 方法不能访问非 static 成员,也不能用
-
代码块 & 顺序
- new 对象时,实例字段默认值 → 实例字段就地初始化 → 实例代码块 → 构造方法。
-
内部类
- 实例内部类创建需要外部类对象:
outer.new Inner()。 - 静态内部类可以直接
new Outer.Inner()。
- 实例内部类创建需要外部类对象:
-
toString
- 想赋予打印对象除了内存地址外实际的意义,可以重写
toString()。
- 想赋予打印对象除了内存地址外实际的意义,可以重写
更多推荐



所有评论(0)