当前位置: 首页 > news >正文

第 5 篇:深入浅出学 Java 语言(JDK8 版)—— 精通类与对象进阶,掌握 Java 面向对象核心能力

简介:聚焦 Java 面向对象核心的“类与对象”进阶知识,从类的完整定义(字段、方法、构造函数)、对象的实例化与使用,到 this 关键字、访问控制、类成员与实例成员,再到嵌套类(静态/内部/局部/匿名类)、Lambda 表达式与方法引用,最后到枚举类型,结合 JDK8 代码示例与通俗解析,帮初学者从基础到进阶吃透类与对象,筑牢 Java 面向对象编程核心能力。

一、类的定义:Java 面向对象的“蓝图”

在 Java 中,类是创建对象的“蓝图”,它定义了对象的状态(字段)和行为(方法)。一个完整的类结构包含修饰符、类名、父类(可选)、接口(可选)和类体,类体中又包含字段、构造函数和方法,这三部分共同构成对象的“骨架”。

1. 类的核心组件

以经典的 Bicycle 类为例,它清晰展示了类的核心组成:

public class Bicycle {// 成员变量(字段):存储对象状态private int cadence;private int gear;private int speed;// 构造函数:初始化对象public Bicycle(int startCadence, int startSpeed, int startGear) {this.gear = startGear; // this 区分字段与参数this.cadence = startCadence;this.speed = startSpeed;}// 方法:定义对象行为public void setCadence(int newValue) {cadence = newValue;}public void speedUp(int increment) {speed += increment;}public int getSpeed() {return speed;}
}
  • 成员变量(字段):分为实例变量(非静态,每个对象独有)和类变量(静态,所有对象共享)。按封装原则,通常用 private 修饰,通过 getter/setter 方法间接访问,避免直接暴露内部状态。
  • 方法:包含修饰符、返回类型、方法名、参数列表和方法体。Java 支持方法重载——多个方法同名但参数列表(数量/类型)不同,例如 DataArtist 类的 draw(String s)draw(int i)
  • 构造函数:与类名相同,无返回类型,用于初始化对象。若未显式定义,编译器会生成无参默认构造函数;若类有父类,默认构造函数会调用父类的无参构造函数(如隐式父类 Object 的构造函数)。

2. 访问控制:控制成员的可见范围

访问修饰符决定了类及成员的访问权限,是封装的核心手段,四种修饰符的访问范围如下:

  • public:对所有类可见,常用于对外提供的接口方法或常量。
  • protected:本类、同包类、子类可见,适合子类需要继承的成员。
  • 默认(无修饰符):仅本类和同包类可见,属于包级私有。
  • private:仅本类可见,用于隐藏内部实现细节(如 Bicyclecadence 字段)。

例如,若 Bicyclespeed 字段用 private 修饰,其他类无法直接修改,只能通过 speedUpapplyBrake 方法控制,确保速度不会被非法修改(如负数)。

二、对象的生命周期:从创建到回收

类是抽象蓝图,对象是类的具体实例。一个对象的生命周期包含“创建-使用-回收”三个阶段,每个阶段都有明确的语法和规则。

1. 创建对象:声明、实例化与初始化

创建对象需三步:声明引用变量、实例化(new 运算符)、初始化(调用构造函数),例如:

// 1. 声明引用变量(未关联对象,值为 null)
Point originOne;
// 2. 实例化+初始化(new 分配内存,调用构造函数)
originOne = new Point(23, 94);
  • 声明Point originOne 仅告诉编译器变量类型,不创建对象,此时调用 originOne.x 会报错。
  • 实例化new Point(23, 94) 为对象分配内存,并返回内存引用。
  • 初始化:构造函数 Point(int a, int b) 为字段 xy 赋值,确保对象创建时状态合法。

若类有多个构造函数(如 Rectangle 类的无参、带 Point 参数、带宽高参数的构造函数),编译器会根据参数列表匹配对应的构造函数。

2. 使用对象:访问字段与调用方法

对象创建后,通过“对象引用.成员”的语法访问字段或调用方法:

// 访问字段(需权限允许)
System.out.println("矩形宽度:" + rectOne.width);
// 调用方法
rectTwo.move(40, 72); // 修改矩形原点
  • 若在类内部使用字段,可直接用简单名称(如 Rectangle 类中直接写 width);外部需用引用变量(如 rectOne.width)。
  • 方法调用时,参数需与声明的类型和顺序匹配,例如 computePayment(double, double, double, int) 需传入四个对应类型的参数。

3. 垃圾回收:自动释放无用对象

Java 无需手动销毁对象,垃圾回收器(GC)会自动回收“无引用”的对象——当对象没有任何引用指向时(如变量设为 null、变量超出作用域),GC 会在合适时机释放其内存。例如:

// 执行后,new Rectangle() 无引用,可被 GC 回收
int tempHeight = new Rectangle().height;

注意:若对象有多个引用(如 originOnerectTwo.origin 指向同一个 Point 对象),需所有引用都失效,对象才会被回收。

三、类的进阶特性:this、static 与初始化

掌握类的基础后,还需理解 this 关键字、static 修饰符和初始化机制,这些是写出优雅 Java 代码的关键。

1. this 关键字:指代当前对象

this 是实例方法或构造函数中对“当前对象”的引用,主要用于两个场景:

  • 区分字段与参数:当参数名与字段名相同时(如 Point(int x, int y)),用 this.x = x 表示“当前对象的 x 字段”,避免遮蔽。
  • 调用同类构造函数:在构造函数中用 this(参数) 调用其他构造函数,减少代码重复。例如 Rectangle 类的无参构造函数调用四参数构造函数:
    public Rectangle() {this(0, 0, 1, 1); // 调用 Rectangle(int x, int y, int width, int height)
    }
    

2. static 关键字:类成员 vs 实例成员

static 修饰的成员属于“类”而非单个对象,称为类成员,与之对应的是实例成员(无 static,每个对象独有):

  • 静态变量(类变量):所有对象共享,内存中仅一份,通过“类名.变量名”访问(如 Bicycle.numberOfBicycles)。例如用静态变量统计 Bicycle 对象的创建数量:
    private static int numberOfBicycles = 0;
    public Bicycle(...) {id = ++numberOfBicycles; // 每次创建对象,计数+1
    }
    
  • 静态方法(类方法):无需创建对象即可调用(如 Math.sqrt()),不能直接访问实例成员(需通过对象引用),也不能使用 this 关键字。

注意:静态变量通常与 final 结合定义常量(如 static final double PI = 3.14159),常量名全大写,单词间用下划线分隔。

3. 初始化机制:静态块与实例块

除了字段声明时初始化(如 public int width = 0),Java 还支持块初始化,分为两种:

  • 静态初始化块:用 static 修饰,仅在类加载时执行一次,用于初始化静态变量。例如:
    static {// 初始化静态变量的复杂逻辑(如读取配置)config = loadConfig();
    }
    
  • 实例初始化块:无 static 修饰,每次创建对象时执行(在构造函数前),用于多个构造函数共享的初始化逻辑:
    {// 所有构造函数都会执行的初始化validateDefaultValues();
    }
    

四、嵌套类:逻辑分组与封装的进阶

Java 允许在一个类内部定义另一个类(嵌套类),按是否带 static 分为静态嵌套类和内部类,它们的核心作用是“逻辑分组仅用一次的类”和“增强封装”。

1. 静态嵌套类:独立于外部类实例

静态嵌套类用 static 修饰,属于外部类的“静态成员”,与外部类实例无关,访问规则如下:

  • 可直接访问外部类的静态成员(如 OuterClass.staticOuterField),访问实例成员需通过外部类对象引用。
  • 实例化语法:OuterClass.StaticNestedClass obj = new OuterClass.StaticNestedClass(),无需先创建外部类对象。

例如 OuterClass 的静态嵌套类 StaticNestedClass

public class OuterClass {static String staticOuterField = "静态外部字段";static class StaticNestedClass {void accessMembers(OuterClass outer) {System.out.println(outer.outerField); // 需外部类对象System.out.println(staticOuterField); // 直接访问静态成员}}
}

2. 内部类:依赖外部类实例

static 的嵌套类称为内部类,必须依赖外部类实例存在,能直接访问外部类的所有成员(包括 private):

  • 实例化语法:先创建外部类对象,再通过 外部对象.new 内部类() 创建内部类对象:
    OuterClass outer = new OuterClass();
    OuterClass.InnerClass inner = outer.new InnerClass();
    
  • 典型场景:辅助类(如 DataStructure 类的 EvenIterator 内部类,用于遍历数组偶数索引):
    public class DataStructure {private int[] arrayOfInts = new int[15];// 内部类:实现迭代逻辑private class EvenIterator implements Iterator<Integer> {private int nextIndex = 0;public boolean hasNext() {return nextIndex <= arrayOfInts.length - 1;}public Integer next() {Integer val = arrayOfInts[nextIndex];nextIndex += 2; // 步长 2,取偶数索引return val;}}// 使用内部类遍历public void printEven() {Iterator<Integer> iterator = new EvenIterator();while (iterator.hasNext()) {System.out.print(iterator.next() + " ");}}
    }
    

3. 局部类与匿名类:方法内的临时类

  • 局部类:定义在方法或代码块内,仅在当前块中有效,可访问外部类成员和“实际上 final”的局部变量(JDK8+,初始化后值不变)。例如 LocalClassExample 中,PhoneNumber 类仅在 validatePhoneNumber 方法中使用,用于验证手机号。
  • 匿名类:无类名的局部类,是“表达式级别的类”,仅用一次,常用于实现接口(如事件监听)。例如用匿名类实现 HelloWorld 接口:
    HelloWorld frenchGreeting = new HelloWorld() {String name = "tout le monde";public void greet() {greetSomeone(name);}public void greetSomeone(String someone) {System.out.println("Salut " + someone);}
    };
    

注意:局部类和匿名类会产生“遮蔽”问题——若内部类的变量与外部同名,内部变量会遮蔽外部变量,需用“外部类名.this.变量”区分(如 ShadowTest.this.x)。

五、Lambda 表达式与方法引用:简化函数式编程

JDK8 引入的 Lambda 表达式,是对“单方法接口(函数式接口)”的简洁实现,避免匿名类的冗余语法;方法引用则是 Lambda 的进一步简化,直接引用现有方法。

1. Lambda 表达式:匿名方法的简写

Lambda 表达式的语法为 (参数列表) -> 方法体,仅适用于函数式接口(如 Predicate<T>Consumer<T>)。例如用 Lambda 筛选 18-25 岁的男性:

// 函数式接口 Predicate<Person>
Predicate<Person> isEligible = (Person p) -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25;
  • 语法简化:参数类型可省略(p -> ...),单参数可省略括号(p -> ...),单表达式方法体可省略大括号和 return(如上例)。
  • 目标类型:Lambda 的类型由上下文决定(如方法参数类型),例如 printPersons 方法参数为 CheckPerson,则 Lambda 就是 CheckPerson 类型。
  • 访问局部变量:Lambda 只能访问“实际上 final”的局部变量(初始化后值不变),否则编译报错。

2. 方法引用:引用现有方法

当 Lambda 仅调用一个现有方法时,可用方法引用替代,分为四种类型:

类型语法示例
静态方法引用类名::静态方法名Person::compareByAge
特定对象实例方法引用对象::实例方法名myApp::appendStrings2
任意对象实例方法引用类名::实例方法名String::compareToIgnoreCase
构造函数引用类名::newHashSet::new

例如用方法引用排序 Person 数组:

Person[] rosterAsArray = roster.toArray(new Person[0]);
Arrays.sort(rosterAsArray, Person::compareByAge); // 静态方法引用

六、枚举类型:类型安全的常量

枚举(enum)是特殊的类,用于定义一组固定常量,比 static final 常量更安全(避免类型错误),还可包含属性和方法。

1. 枚举的基础使用

定义枚举类型需用 enum 关键字,常量全大写,例如一周的日子:

public enum Day {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}

使用时直接通过“枚举名.常量”访问,配合 switch 语句更清晰:

public void tellItLikeItIs(Day day) {switch (day) {case MONDAY: System.out.println("Mondays are bad."); break;case SATURDAY: case SUNDAY: System.out.println("Weekends are best."); break;default: System.out.println("Midweek days are so-so.");}
}

2. 带属性和方法的枚举

枚举可包含属性、构造函数和方法,例如表示太阳系行星的 Planet 枚举,包含质量、半径属性和计算重力的方法:

public enum Planet {MERCURY(3.303e+23, 2.4397e6),VENUS(4.869e+24, 6.0518e6),EARTH(5.976e+24, 6.37814e6); // 常量列表以分号结尾private final double mass;   // 质量(kg)private final double radius; // 半径(m)// 私有构造函数(枚举构造函数必须私有)Planet(double mass, double radius) {this.mass = mass;this.radius = radius;}// 计算表面重力double surfaceGravity() {final double G = 6.67300E-11; // 万有引力常量return G * mass / (radius * radius);}// 计算物体在该行星的重量double surfaceWeight(double earthWeight) {return earthWeight / EARTH.surfaceGravity() * surfaceGravity();}
}

调用时通过枚举常量调用方法:

double earthWeight = 175;
System.out.println("在水星上的重量:" + Planet.MERCURY.surfaceWeight(earthWeight));

七、问题与练习:巩固类与对象知识

1. 基础问题(自查掌握度)

  1. IdentifyMyParts 类中,public static int x 是____(类变量/实例变量),public int y 是____(类变量/实例变量)?
    答案:类变量、实例变量
  2. 执行以下代码,输出结果是什么?
    IdentifyMyParts a = new IdentifyMyParts();
    IdentifyMyParts b = new IdentifyMyParts();
    a.y = 5; b.y = 6; a.x = 1; b.x = 2;
    System.out.println("a.y=" + a.y + ", b.y=" + b.y + ", a.x=" + a.x + ", IdentifyMyParts.x=" + IdentifyMyParts.x);
    
    答案:a.y=5, b.y=6, a.x=2, IdentifyMyParts.x=2(类变量 x 共享,最后被设为 2)
  3. 修复 SomethingIsWrong 程序的错误:
    public class SomethingIsWrong {public static void main(String[] args) {Rectangle myRect; // 仅声明,未实例化myRect.width = 40;System.out.println("面积:" + myRect.getArea());}
    }
    
    修复:添加实例化 myRect = new Rectangle();

2. 动手练习

  1. 练习1:用枚举重写 Card 类
    定义 Suit 枚举(花色:HEART、DIAMOND、CLUB、SPADE)和 Rank 枚举(点数:ACE、TWO…KING),Card 类包含 suitrank 字段,重写 toString() 方法:
    public class Card {private final Suit suit;private final Rank rank;public Card(Suit suit, Rank rank) {this.suit = suit;this.rank = rank;}@Overridepublic String toString() {return rank + " of " + suit;}// Suit 和 Rank 枚举public enum Suit { HEART, DIAMOND, CLUB, SPADE }public enum Rank { ACE, TWO, THREE, ..., KING }
    }
    
  2. 练习2:实现 Deck 类
    Deck 类包含 List<Card> 字段,在构造函数中初始化 52 张牌(4 种花色 × 13 种点数),提供 shuffle()(洗牌)和 deal()(发牌)方法:
    public class Deck {private List<Card> cards = new ArrayList<>();public Deck() {// 初始化 52 张牌for (Card.Suit suit : Card.Suit.values()) {for (Card.Rank rank : Card.Rank.values()) {cards.add(new Card(suit, rank));}}}public void shuffle() {Collections.shuffle(cards);}public Card deal() {return cards.isEmpty() ? null : cards.remove(0);}
    }
    

八、总结:类与对象是 Java 面向对象的基石

Java 面向对象的核心是“用类描述事物,用对象模拟实例”——类定义了事物的共性(字段+方法),对象是共性的具体体现;通过封装(访问控制)隐藏细节,通过嵌套类和枚举优化代码结构,通过 Lambda 简化函数式编程。

掌握本文内容后,你已具备 Java 面向对象的核心能力:能定义符合封装原则的类、灵活创建和使用对象、用进阶特性(this、static、嵌套类)优化代码、用枚举保证常量安全。后续学习集合、框架时,这些知识将成为你理解复杂逻辑的基础。


文章转载自:

http://Ksm5ouWt.Lrdzb.cn
http://a6WLfcgA.Lrdzb.cn
http://PHLTWEea.Lrdzb.cn
http://NH2WcK8s.Lrdzb.cn
http://kj5u7e2W.Lrdzb.cn
http://iYB1C0IE.Lrdzb.cn
http://G3I8zIbt.Lrdzb.cn
http://JuojF3iu.Lrdzb.cn
http://d4iFod3Z.Lrdzb.cn
http://tsk0m39N.Lrdzb.cn
http://uuCRvnup.Lrdzb.cn
http://7wuoh4hk.Lrdzb.cn
http://5dvKkmDF.Lrdzb.cn
http://juBuMrk0.Lrdzb.cn
http://Wl2xQoMU.Lrdzb.cn
http://ZcqK2HxN.Lrdzb.cn
http://LBCqUHC5.Lrdzb.cn
http://JoBayjEO.Lrdzb.cn
http://cNa4HmYR.Lrdzb.cn
http://B29kkvpm.Lrdzb.cn
http://l4Ewi96h.Lrdzb.cn
http://bATyEegq.Lrdzb.cn
http://gIlLLaVB.Lrdzb.cn
http://pUNLYaZG.Lrdzb.cn
http://oThyQ6VB.Lrdzb.cn
http://J1nZf3TG.Lrdzb.cn
http://pwMbE0zs.Lrdzb.cn
http://cSkL9XDu.Lrdzb.cn
http://Sk7Ybe1T.Lrdzb.cn
http://iCU3AxSR.Lrdzb.cn
http://www.dtcms.com/a/379989.html

相关文章:

  • Gin-Vue-Admin学习笔记
  • Golang關於信件的
  • The 2024 ICPC Asia East Continent Online Contest (I)
  • 【数所有因子和快速新解/范围亲密数/分解因式怎么去掉重复项】2022-10-31
  • SQL语句执行时间太慢,有什么优化措施?以及衍生的相关问题
  • 【论文阅读】Language-Guided Image Tokenization for Generation
  • PHP:从入门到实战的全方位指南
  • 经典动态规划题解
  • 商城购物系统自动化测试报告
  • [工作表控件20] 拼音排序功能:中文数据高效检索实战指南
  • 9120 部 TMDb 高分电影数据集 | 7 列全维度指标 (评分 / 热度 / 剧情)+API 权威源 | 电影趋势分析 / 推荐系统 / NLP 建模用
  • 【Java】多态
  • LeetCode热题 438.找到字符中所有字母异位词 (滑动窗口)
  • 解决 N1 ARMBIAN Prometheus 服务启动失败问题
  • Linux 正则表达式详解(基础 + 扩展 + 实操)
  • 01.【Linux系统编程】Linux初识(Linux内核版本、基础指令、理论知识、shell命令及运行原理)
  • MATLAB 的无人机 PID 控制及智能 PID 控制器设计的仿真
  • D007 django+neo4j三维知识图谱医疗问答系统|3D+2D双知识图谱可视化+问答+寻医问药系统
  • 5G单兵图传 5G单兵 单兵图传 无线图传 无线图传方案 无人机图传解决方案 指挥中心大屏一目了然
  • npm / yarn / pnpm 包管理器对比与最佳实践(含国内镜像源配置与缓存优化)
  • 运维安全06 - 服务安全
  • nestjs(node.js) 跟 java 关于return 的JSON 数据转换
  • RabbitMQ---面试题
  • npm ERR! code CERT_HAS_EXPIRED
  • Windows、Linux 系统 nodejs 和 npm 版本更新及错误修复
  • 网站漏洞扫描要怎么处理?
  • 无线通信模块撑油库安全:传液位信号,简布线与后期维护工作
  • ruoyi-vue(十四)——前端框架及package.json,vite.config.js, main.js文件介绍
  • 【计算机网络 | 第15篇】动态主机配置协议
  • 七层网络协议-面试