
JavaSE(三)面向对象编程
在 Java 中,面向对象(Object-Oriented Programming,OOP)是一种核心编程思想,以下按照从基础到进阶的顺序详细介绍:
一、面向对象基础
1.类与对象
- 类:是对一类事物的抽象描述,是概念上的定义。比如 “人类”,它包含了所有人共有的特征(属性)和行为(方法) 。在 Java 代码中,定义类使用
class关键字,例如定义一个简单的人类:
new关键字来创建对象,比如创建一个名为 “小明” 的人:
public class Main {
public static void main(String[] args) {
Person xiaoming = new Person();
xiaoming.name = "小明";
xiaoming.age = 20;
xiaoming.sex = "男";
}
}
2.对象创建与使用
-
创建方式:
-
先声明再创建:
Person person; person = new Person();,先声明一个Person类型的变量person,此时它还未指向任何对象,然后通过new关键字在堆内存中创建一个Person对象,并将其引用赋值给person变量。 - 直接创建:
Person person = new Person();,一步完成对象的声明和创建。 - 使用方式:通过对象引用加
.运算符来访问对象的属性和方法。比如获取 “小明” 的年龄:
3.成员变量与局部变量
- 成员变量:定义在类中方法外的变量,属于类的成员。如上述
Person类中的name、age、sex。成员变量有默认值(数值类型为 0 或 0.0,布尔类型为false,引用类型为null),并且随着对象的创建而存在,随对象的销毁而消失。 - 局部变量:定义在方法体内部、代码块、形参、构造器中的变量。例如:
public class Main {
public static void main(String[] args) {
int num = 10; // num是局部变量
{
int localVar = 20; // localVar也是局部变量,作用域在这个代码块内
}
// System.out.println(localVar); // 这里会报错,因为localVar超出作用域
}
}
局部变量没有默认值,必须先赋值才能使用,其生命周期伴随着所在代码块的执行而创建,执行结束而销毁。
4.成员方法
- 定义:方法是类或对象行为特征的抽象,用来完成某个功能操作,将功能封装成方法可以实现代码复用,减少冗余。例如在
Person类中添加一个介绍自己的方法:
public class Person {
String name;
int age;
String sex;
public void introduce() {
System.out.println("我叫" + name + ",今年" + age + "岁,性别是" + sex + "。");
}
}
- 调用:通过对象引用来调用成员方法,如:
public class Main {
public static void main(String[] args) {
Person xiaoming = new Person();
xiaoming.name = "小明";
xiaoming.age = 20;
xiaoming.sex = "男";
xiaoming.introduce();
}
}
5.构造器
- 作用:用于创建对象并初始化对象的属性。构造器的方法名与类名相同,没有返回值(也不写
void)。 - 示例:给
Person类添加构造器:
public class Person {
String name;
int age;
String sex;
// 无参构造器
public Person() {}
// 有参构造器
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
创建对象时可以使用构造器进行初始化:
public class Main {
public static void main(String[] args) {
Person xiaoming = new Person("小明", 20, "男");
xiaoming.introduce();
}
}
6.this关键字
- 作用:代表当前对象,用来区分成员变量和局部变量重名的情况,也可以用于在一个构造器中调用另一个构造器 。例如:
public class Person {
String name;
int age;
String sex;
public Person(String name, int age, String sex) {
this.name = name; // 这里的this.name指的是成员变量name
this.age = age;
this.sex = sex;
}
public Person(String name, int age) {
this(name, age, "未知"); // 调用上面的有参构造器
}
}
二、面向对象高级
1.类变量和类方法(static 修饰)
- 类变量(静态变量):被
static修饰,是该类的所有对象共享的变量。定义语法如public static int count;。可以通过类名.类变量名或对象名.类变量名的方式访问。类变量随着类的加载而创建,即使没有创建对象实例也可以访问。 - 类方法(静态方法):同样被
static修饰,调用形式为类名.类方法名或对象名.类方法名(前提是满足访问权限)。当方法中不涉及和对象相关的成员时,可将方法设计成静态方法,比如工具类中的方法。在类方法中不能使用this和super关键字,也只能访问静态变量或静态方法。例如:
public class Utils {
public static int add(int a, int b) {
return a + b;
}
}
// 调用
public class Main {
public static void main(String[] args) {
int result = Utils.add(3, 5);
System.out.println(result);
}
}
2.代码块
- 基本介绍:代码块是使用
{}括起来的一段代码,分为普通代码块、静态代码块。 - 静态代码块:使用
static修饰,在类加载时执行,且只执行一次,通常用于初始化类的静态资源。例如:
public class MyClass {
static {
System.out.println("静态代码块执行");
}
public MyClass() {
System.out.println("构造器执行");
}
}
// 测试
public class Main {
public static void main(String[] args) {
MyClass obj1 = new MyClass();
MyClass obj2 = new MyClass();
}
}
// 输出结果:静态代码块执行 构造器执行 构造器执行
- 普通代码块:在方法或构造器中定义的代码块,每次执行到代码块时都会执行,用于限定变量的作用域等。
3.final 关键字
- 基本介绍:可以用来修饰类、方法和变量。
- 修饰类:表示该类不能被继承,如
java.lang.String类就是被final修饰的,不能有子类。 - 修饰方法:表示该方法不能被重写,确保方法的行为在子类中不会被改变。
- 修饰变量:表示该变量一旦赋值后就不能再被重新赋值,如果修饰的是基本数据类型,值不可变;如果修饰的是引用数据类型,引用地址不可变,但对象内部的属性可以改变。
4.包装类的介绍

其中能够表示数字的基本类型包装类,继承自Number类,对应关系如下表:
- byte -> Byte
- boolean -> Boolean
- short -> Short
- char -> Character
- int -> Integer
- long -> Long
- float -> Float
- double -> Double
包装类实际上就是将我们的基本数据类型,封装成一个类(运用了封装的思想)我们可以来看看Integer类中是怎么写的:
包装类型支持自动装箱,我们可以直接将一个对应的基本类型值作为对应包装类型引用变量的值:
这是怎么做到的?为什么一个对象类型的值可以直接接收一个基本类类型的值?实际上这里就是自动装箱:
这里本质上就是被自动包装成了一个Integer类型的对象,只是语法上为了简单,就支持像这样编写。既然能装箱,也是支持拆箱的:
实际上上面的写法本质上就是:
这里就是自动拆箱,得益于包装类型的自动装箱和拆箱机制,我们可以让包装类型轻松地参与到基本类型的运算中:
public static void main(String[] args) {
Integer a = 10, b = 20;
int c = a * b; //直接自动拆箱成基本类型参与到计算中
System.out.println(c);
}
因为包装类是一个类,不是基本类型,所以说两个不同的对象,那么是不相等的:
public static void main(String[] args) {
Integer a = new Integer(10);
Integer b = new Integer(10);
System.out.println(a == b); //虽然a和b的值相同,但是并不是同一个对象,所以说==判断为假
}
那么自动装箱的呢?
我们发现,通过自动装箱转换的Integer对象,如果值相同,得到的会是同一个对象,这是因为:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high) //这里会有一个IntegerCache,如果在范围内,那么会直接返回已经提前创建好的对象
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache会默认缓存-128~127之间的所有值,将这些值提前做成包装类放在数组中存放,虽然我们目前还没有学习数组,但是各位小伙伴只需要知道,我们如果直接让 -128~127之间的值自动装箱为Integer类型的对象,那么始终都会得到同一个对象,这是为了提升效率,因为小的数使用频率非常高,有些时候并不需要创建那么多对象,创建对象越多,内存也会消耗更多。
4.抽象类
- 定义:使用
abstract修饰的类,抽象类不能被实例化,通常包含抽象方法(也可以包含非抽象方法)。抽象方法只有方法声明,没有方法体,使用abstract关键字修饰。例如:
public abstract class Animal {
public abstract void speak(); // 抽象方法
public void eat() { // 非抽象方法
System.out.println("动物进食");
}
}
- 子类实现:子类继承抽象类后,必须重写抽象类中的所有抽象方法,否则子类也必须声明为抽象类。
6.接口
- 基本介绍:接口是一种特殊的抽象类型,只包含常量和抽象方法(JDK 8 及以后可以有默认方法和静态方法)。接口使用
interface关键字定义,类实现接口使用implements关键字。例如:
public interface Flyable {
int MAX_SPEED = 100; // 接口中的常量默认是public static final修饰
void fly(); // 抽象方法默认是public abstract修饰
}
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("鸟儿在飞翔");
}
}
- 特点:接口可以实现多继承,即一个类可以实现多个接口,弥补了 Java 单继承的不足,并且接口主要用于定义行为规范,让不同的类按照统一的规范去实现相关功能。
7.内部类
- 基本介绍:定义在另一个类内部的类,分为成员内部类、局部内部类、匿名内部类和静态内部类。
- 成员内部类:作为外部类的成员,可以访问外部类的所有成员,包括私有成员。创建成员内部类对象需要先创建外部类对象,例如:
public class Outer {
private int num = 10;
public class Inner {
public void show() {
System.out.println("外部类的num: " + num);
}
}
}
// 创建对象
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.show();
}
}
- 局部内部类:定义在方法或代码块中的类,作用域仅限于所在的方法或代码块,不能使用访问修饰符,只能访问所在方法中被
final或等效于final(JDK 8 后实际上不需要显式final)修饰的局部变量。 - 匿名内部类:没有类名的内部类,通常用于创建接口或抽象类的实例,并且只使用一次。例如:
public interface OnClickListener {
void onClick();
}
// 使用匿名内部类
public class Main {
public static void main(String[] args) {
OnClickListener listener = new OnClickListener() {
@Override
public void onClick() {
System.out.println("点击事件触发");
}
};
listener.onClick();
}
}
静态内部类:使用static修饰的内部类,它不依赖于外部类的实例,可以直接创建对象,并且只能访问外部类的静态成员