02_Java 对象和类
Java 类定义、对象创建、实例变量与方法、构造器、面向对象基本概念
Java 作为一种面向对象的编程语言,支持以下基本概念:
1、类(Class):
- 定义对象的蓝图,包括属性和方法。
- 示例:
public class Car { ... }
2、对象(Object):
- 类的实例,具有状态和行为。
- 示例:
Car myCar = new Car();
3、继承(Inheritance):
- 一个类可以继承另一个类的属性和方法。
- 示例:
public class Dog extends Animal { ... }
4、封装(Encapsulation):
- 将对象的状态(字段)私有化,通过公共方法访问。
- 示例:
private String name; public String getName() { return name; }
5、多态(Polymorphism):
- 对象可以表现为多种形态,主要通过方法重载和方法重写实现。
- 示例:
- 方法重载:
public int add(int a, int b) { ... }和public double add(double a, double b) { ... } - 方法重写:
@Override public void makeSound() { System.out.println("Meow"); }
- 方法重载:
6、抽象(Abstraction):
- 使用抽象类和接口来定义必须实现的方法,不提供具体实现。
- 示例:
- 抽象类:
public abstract class Shape { abstract void draw(); } - 接口:
public interface Animal { void eat(); }
- 抽象类:
7、接口(Interface):
- 定义类必须实现的方法,支持多重继承。
- 示例:
public interface Drivable { void drive(); }
8、方法(Method):
- 定义类的行为,包含在类中的函数。
- 示例:
public void displayInfo() { System.out.println("Info"); }
9、方法重载(Method Overloading):
- 同一个类中可以有多个同名的方法,但参数不同。
- 示例:
public class MathUtils { public int add(int a, int b) { return a + b; } public double add(double a, double b) { return a + b; } }
Java 中的类
类可以看成是创建 Java 对象的模板。 ![[java-对象和类.png]]
通过上图创建一个简单的类来理解下 Java 中类的定义:
public class Dog {
String breed;
int size;
String colour;
int age;
void eat() {
}
void run() {
}
void sleep(){
}
void name(){
}
}
一个类可以包含以下类型变量:
- 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
- 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
- 类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。
一个类可以拥有多个方法,在上面的例子中:eat()、run()、sleep() 和 name() 都是 Dog 类的方法。
创建对象
对象是根据类创建的。在Java中,使用关键字 new 来创建一个新的对象。创建对象需要以下三步:
- 声明:声明一个对象,包括对象名称和对象类型。
- 实例化:使用关键字 new 来创建一个对象。
- 初始化:使用 new 创建对象时,会调用构造方法初始化对象。
下面是一个创建对象的例子
public class Puppy{
public Puppy(String name){
//这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public static void main(String[] args){
// 下面的语句将创建一个Puppy对象
Puppy myPuppy = new Puppy( "tommy" );
}
}
编译并运行上面的程序,会打印出下面的结果:
小狗的名字是 : tommy
访问实例变量和方法
通过已创建的对象来访问成员变量和成员方法,如下所示:
/* 实例化对象 */
Object referenceVariable = new Constructor();
/* 访问类中的变量 */
referenceVariable.variableName;
/* 访问类中的方法 */
referenceVariable.methodName();
使用 Object 类型声明变量只能在编译时访问 Object 类中的方法和属性,但在运行时,你可以通过强制类型转换将其转换为特定类型,以便访问特定类型的方法和属性。
实例
下面的例子展示如何访问实例变量和调用成员方法:
public class Puppy {
private String name;
private int age
// 构造器
public Puppy (String name) {
this.name = name;
}
// age 设置
public void setAge(int age) {
this.age = age;
}
// age 获取
public int getAge() {
return age;
}
// name 获取
public String getName() {
return name;
}
public static void main(String[] args) {
// 创建对象
Puppy myDog = new Puppy("Lily");
// 通过方法设定age
myDog.setAge(2)
// 调用另一个方法获取age
int age = myDog.getAge()
System.out.println("小狗的年龄是" + age)
//也可以直接访问成员变量
System.out.println("变量值:" + myDog.getAge())
}
}
编译并运行上面的程序,产生如下结果:
小狗的名字是 : Lily
小狗的年龄为 : 2
变量值 : 2
源文件声明规则
在本节的最后部分,我们将学习源文件的声明规则。当在一个源文件中定义多个类,并且还有 import 语句和 package 语句时,要特别注意这些规则。
- 一个源文件中只能有一个 public 类
- 一个源文件可以有多个非 public 类
- 源文件的名称应该和 public 类的类名保持一致。例如:源文件中 public 类的类名是 Employee,那么源文件应该命名为Employee.java。
- 如果一个类定义在某个包中,那么 package 语句应该在源文件的首行。
- 如果源文件包含 import 语句,那么应该放在 package 语句和类定义之间。如果没有 package 语句,那么 import 语句应该在源文件中最前面。
- import 语句和 package 语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。
类有若干种访问级别,并且类也分不同的类型:抽象类和 final 类等。这些将在访问控制章节介绍。
除了上面提到的几种类型,Java 还有一些特殊的类,如:内部类、匿名类。
构造方法(Constructor)
构造方法是创建对象时自动调用的特殊方法,用于初始化对象的状态。
规则:
- 方法名必须和类名完全相同
- 没有返回类型(连
void也没有) - 每个类至少有一个构造方法;如果不写,Java 会提供默认的无参构造方法
无参与有参构造方法
public class Car {
String brand;
int speed;
// 无参构造
public Car() {
brand = "未知";
speed = 0;
}
// 有参构造
public Car(String brand, int speed) {
this.brand = brand; // this 指向当前对象,区分同名参数和成员变量
this.speed = speed;
}
}
Car c1 = new Car();
Car c2 = new Car("Toyota", 120);
构造方法重载与链式调用(this())
public class Car {
String brand;
int speed;
String color;
public Car(String brand, int speed, String color) {
this.brand = brand;
this.speed = speed;
this.color = color;
}
// this() 调用其他构造方法,必须是第一行
public Car(String brand, int speed) {
this(brand, speed, "白色");
}
public Car(String brand) {
this(brand, 0);
}
}
Car c1 = new Car("BMW", 200, "黑色");
Car c2 = new Car("Audi", 180); // color 默认白色
Car c3 = new Car("Tesla"); // speed 默认 0,color 默认白色
this 与 super 关键字
this 关键字
this 指向当前对象,有三个主要用途:
public class Person {
String name;
int age;
// 用途1:区分同名局部变量与实例变量
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 用途2:在本类方法中调用其他方法
public void introduce() {
System.out.println("我是 " + this.name + ",今年 " + this.getAge() + " 岁");
}
public int getAge() { return this.age; }
// 用途3:将当前对象传递给其他方法
public Person getSelf() { return this; }
}
super 关键字
super 指向父类,用于访问父类成员或调用父类构造方法:
class Animal {
String name;
Animal(String name) { this.name = name; }
void introduce() {
System.out.println("我是动物,名字叫 " + name);
}
}
class Dog extends Animal {
String breed;
Dog(String name, String breed) {
super(name); // 调用父类构造方法,必须是第一行
this.breed = breed;
}
@Override
void introduce() {
super.introduce(); // 调用父类的 introduce()
System.out.println("我是 " + breed + " 犬");
}
}
Dog d = new Dog("旺财", "金毛");
d.introduce();
// 我是动物,名字叫 旺财
// 我是 金毛 犬
toString()、equals() 与 hashCode()
这三个方法来自所有类的基类 Object,通常需要在自定义类中重写。
toString()——对象的字符串表示
public class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
// 不重写时,打印结果类似 Point@7ef88735
@Override
public String toString() {
return "Point(" + x + ", " + y + ")";
}
}
Point p = new Point(3, 4);
System.out.println(p); // Point(3, 4)
equals()——对象内容比较
== 比较引用(内存地址),equals() 比较内容:
public class Student {
String id;
Student(String id) { this.id = id; }
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // 同一对象直接返回
if (!(obj instanceof Student)) return false; // 类型不匹配
Student other = (Student) obj;
return this.id.equals(other.id);
}
}
Student s1 = new Student("001");
Student s2 = new Student("001");
System.out.println(s1 == s2); // false(不同对象)
System.out.println(s1.equals(s2)); // true(id 相同)
hashCode()——与 equals() 配套
重要规则: 重写 equals() 时必须同时重写 hashCode()。否则存入 HashMap / HashSet 时行为错误。
@Override
public int hashCode() {
return id.hashCode(); // 依赖同一字段保证一致性
}
实际开发: 使用 IDE 右键 → Generate → equals() and hashCode(),或使用 Java 16+ 的
record自动生成。
Records(Java 16+)
Record 是专为"只存数据"设计的不可变类,自动生成构造方法、getter、toString、equals、hashCode。
// 传统写法:大量样板代码
public class Point {
private final int x;
private final int y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int x() { return x; }
public int y() { return y; }
@Override public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
// 还需要 equals 和 hashCode...
}
// Record 写法:一行搞定
public record Point(int x, int y) {}
// 使用方式完全一样
Point p = new Point(3, 4);
System.out.println(p.x()); // 3
System.out.println(p.y()); // 4
System.out.println(p); // Point[x=3, y=4]
System.out.println(new Point(3, 4).equals(new Point(3, 4))); // true
Record 的限制:
- 字段是
final的,不可变,没有 setter - 不能继承其他类(但可以实现接口)
- 适合做 DTO(数据传输对象)、坐标、配置项等值对象
面向对象概念速查
| 概念 | 关键字 / 语法 | 要点 |
|---|---|---|
| 构造方法 | ClassName() |
与类同名,无返回类型 |
| 调用本类构造 | this() |
必须是第一行 |
| 调用父类构造 | super() |
必须是第一行 |
| 当前对象引用 | this |
区分同名变量、传递自身 |
| 父类引用 | super |
访问父类成员或构造 |
| 内容比较 | equals() |
需重写;== 比地址 |
| 打印对象 | toString() |
重写后 println 自动调用 |
| 不可变数据类 | record |
Java 16+,自动生成所有样板代码 |