# JavaSE核心知识点02面向对象编程
🤟致敬读者
- 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉
📘博主相关
- 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息
文章目录
- JavaSE核心知识点02面向对象编程
- JavaSE核心知识点02面向对象编程02-01(类与对象)
- **一、类(Class):设计蓝图**
- 1. **什么是类?**
- 2. **如何定义一个类?**
- **二、对象(Object):类的实例**
- 1. **什么是对象?**
- 2. **如何创建对象?**
- **三、实例变量 vs 静态变量**
- 1. **实例变量(对象级别)**
- 2. **静态变量(类级别)**
- **四、封装(Encapsulation)**
- 1. **为什么要封装?**
- 2. **实现封装的步骤:**
- **五、构造方法(Constructor)**
- 1. **什么是构造方法?**
- 2. **默认构造方法**
- 3. **自定义构造方法**
- **六、继承(Inheritance)**
- **七、对象的内存分配**
- **八、常见误区**
- **九、最佳实践**
- **十、练习题目**
- JavaSE核心知识点02面向对象编程02-02(封装、继承、多态)
- **一、封装(Encapsulation)**
- **1. 什么是封装?**
- **2. 如何实现封装?**
- **3. 代码示例**
- **4. 封装的好处**
- **二、继承(Inheritance)**
- **1. 什么是继承?**
- **2. 代码示例**
- **3. 继承的注意事项**
- **三、多态(Polymorphism)**
- **1. 什么是多态?**
- **2. 方法重写(Override)**
- **3. 向上转型(Upcasting)**
- **4. 多态的优势**
- **四、总结与对比**
- **五、常见问题**
- **1. 方法重载(Overload) vs 方法重写(Override)**
- **2. 什么时候用继承?**
- JavaSE核心知识点02面向对象编程02-03(抽象类与接口)
- 一、抽象类(Abstract Class)
- **1. 什么是抽象类?**
- **2. 抽象方法**
- **3. 继承抽象类**
- **4. 使用场景**
- 二、接口(Interface)
- **1. 什么是接口?**
- **2. 定义接口**
- **3. 实现接口**
- **4. 多实现**
- **5. 使用场景**
- 三、抽象类 vs 接口:核心区别
- 四、如何选择抽象类 vs 接口?
- **1. 用抽象类的情况**
- **2. 用接口的情况**
- 五、实际案例
- **场景**:设计一个游戏中的角色系统
- 六、常见误区
- 七、总结
- JavaSE核心知识点02面向对象编程02-04(包和导入)
- **一、包(Package)的作用**
- **二、定义包**
- **三、导入(Import)其他包的类**
- **四、常见场景与注意事项**
- **1. 同一包内的类无需导入**
- **2. 不同包中的类必须导入**
- **3. 处理同名类冲突**
- **五、默认包(不推荐使用)**
- **六、包与访问权限**
- **七、实战练习**
- **八、常见问题解答**
- JavaSE核心知识点02面向对象编程02-05(方法)
- **一、方法是什么?**
- **二、方法的组成**
- **三、如何定义方法?**
- **1. 无参数、无返回值的方法**
- **2. 带参数、有返回值的方法**
- **3. 多个参数的方法**
- **四、如何调用方法?**
- **1. 调用静态方法(`static`修饰)**
- **2. 调用非静态方法(需要对象实例)**
- **五、方法的重载(Overload)**
- **六、递归方法(Recursion)**
- **七、常见问题解答**
- **1. 方法是否需要返回值?**
- **2. 参数传递是“值传递”还是“引用传递”?**
- **3. 静态方法 vs 非静态方法?**
- **八、总结与练习**
- JavaSE核心知识点02面向对象编程02-06(泛型)
- **一、为什么需要泛型?**
- **二、泛型的基本语法**
- **1. 定义泛型类/接口**
- **2. 使用泛型类**
- **三、泛型方法**
- **四、泛型通配符 `?`**
- **1. 无界通配符 `<?>`**
- **2. 上界通配符 `<? extends T>`**
- **3. 下界通配符 `<? super T>`**
- **PECS 原则**(Producer Extends, Consumer Super)
- **五、类型擦除(Type Erasure)**
- **六、泛型的限制与注意事项**
- **七、典型应用场景**
- **八、代码示例:自定义泛型栈**
- **九、总结**
- JavaSE核心知识点02面向对象编程02-07(枚举)
- **一、泛型存在的意义**
- **二、泛型核心语法**
- **三、类型通配符**
- **四、类型擦除与限制**
- **五、泛型高级应用**
- **六、最佳实践与常见错误**
- **七、学习建议**
- JavaSE核心知识点02面向对象编程02-8(异常处理)
- **一、异常处理的核心概念**
- **1. 什么是异常?**
- **2. Java异常分类**
- **二、异常处理的关键字**
- **1. try-catch-finally**
- **2. throws**
- **3. throw**
- **三、常见异常类型与处理**
- **1. 常见异常示例**
- **2. 处理多个异常**
- **四、自定义异常**
- **1. 创建自定义异常类**
- **2. 使用自定义异常**
- **五、最佳实践与常见陷阱**
- **1. 最佳实践**
- **2. 常见陷阱**
- **六、总结与练习**
- **1. 关键点总结**
- **2. 练习题目**
📃文章前言
- 🔷文章均为学习工作中整理的笔记。
- 🔶如有错误请指正,共同学习进步。
JavaSE核心知识点02面向对象编程
JavaSE核心知识点02面向对象编程02-01(类与对象)
**类(Class)和对象(Object)**是面向对象编程(OOP)的核心基础。以下用通俗易懂的方式,结合代码示例和生活中的例子,帮你彻底掌握它们!
一、类(Class):设计蓝图
1. 什么是类?
- 类是对象的模板或蓝图,定义了对象的属性(数据)和行为(方法)。
- 例如:设计一辆汽车前,你需要图纸(类),图纸规定了汽车的品牌、颜色(属性)和启动、刹车(方法)。
2. 如何定义一个类?
public class Dog {// 成员变量(属性)String name;int age;// 方法(行为)void bark() {System.out.println(name + "汪汪叫!");}
}
- 成员变量:描述对象的属性(如
name
,age
)。 - 方法:描述对象能做什么(如
bark()
)。
二、对象(Object):类的实例
1. 什么是对象?
- 对象是根据类创建的具体实例,占用内存空间。
- 例如:根据
Dog
类可以创建具体的狗对象(如“小黑,3岁”)。
2. 如何创建对象?
public class Main {public static void main(String[] args) {// 创建Dog类的对象Dog myDog = new Dog();// 设置对象的属性myDog.name = "小黑";myDog.age = 3;// 调用对象的方法myDog.bark(); // 输出:小黑汪汪叫!}
}
new
关键字:在内存中分配空间,创建对象。.
操作符:访问对象的属性和方法。
三、实例变量 vs 静态变量
1. 实例变量(对象级别)
- 每个对象拥有独立的副本。
- 例如:每只狗的
name
和age
不同。
2. 静态变量(类级别)
- 用
static
修饰,所有对象共享同一份数据。 - 例如:统计所有狗的数量。
public class Dog {String name;int age;static int totalDogs = 0; // 静态变量public Dog() {totalDogs++; // 每创建一只狗,总数+1}
}// 使用:
Dog dog1 = new Dog();
Dog dog2 = new Dog();
System.out.println(Dog.totalDogs); // 输出2(通过类名访问)
四、封装(Encapsulation)
1. 为什么要封装?
- 保护数据,防止外部直接修改。
- 例如:狗的年龄不能为负数,通过方法控制。
2. 实现封装的步骤:
- 私有化成员变量:用
private
修饰。 - 提供公共方法:
getter
和setter
。
public class Dog {private String name;private int age;// Getter方法(读取属性)public String getName() {return name;}// Setter方法(设置属性)public void setAge(int age) {if (age >= 0) { // 校验合法性this.age = age;} else {System.out.println("年龄不能为负数!");}}
}// 使用:
Dog dog = new Dog();
dog.setAge(-1); // 输出:年龄不能为负数!
五、构造方法(Constructor)
1. 什么是构造方法?
- 用于初始化对象,在
new
时自动调用。 - 方法名与类名相同,没有返回类型。
2. 默认构造方法
- 如果类中没有定义构造方法,Java会提供默认的无参构造方法。
- 例如:
Dog dog = new Dog();
3. 自定义构造方法
public class Dog {private String name;private int age;// 自定义构造方法public Dog(String name, int age) {this.name = name;this.age = age;}
}// 使用:
Dog myDog = new Dog("小黑", 3); // 直接初始化属性
六、继承(Inheritance)
(先简单了解,后续可深入学习)
- 子类继承父类的属性和方法,实现代码复用。
public class Animal { // 父类void eat() {System.out.println("动物吃东西");}
}public class Dog extends Animal { // 子类void bark() {System.out.println("狗叫");}
}// 使用:
Dog dog = new Dog();
dog.eat(); // 继承自Animal类
七、对象的内存分配
- 栈内存:存储对象的引用变量(如
Dog myDog
)。 - 堆内存:存储实际对象的数据(通过
new
分配)。
Dog dog1 = new Dog(); // dog1引用指向堆中的对象
Dog dog2 = dog1; // dog2和dog1指向同一个对象
八、常见误区
-
混淆类和对象:
- 类:
Dog
(设计图)。 - 对象:
new Dog()
(具体的狗)。
- 类:
-
误用静态变量:
- 静态变量属于类,不要通过对象访问(用
类名.变量名
)。
- 静态变量属于类,不要通过对象访问(用
九、最佳实践
- 单一职责原则:一个类只做一件事。
- 合理封装:尽量将成员变量设为
private
。 - 优先使用实例变量:除非需要共享数据,否则不用
static
。
十、练习题目
- 定义一个
Student
类,包含姓名、学号、年龄属性,并提供方法打印学生信息。 - 创建一个
BankAccount
类,实现存款、取款、查询余额功能,并保证余额不被直接修改。
通过以上内容,你已经掌握了Java类与对象的核心概念!接下来可以尝试编写小项目(如学生管理系统),巩固知识!如果有疑问,随时问我哦 😊
JavaSE核心知识点02面向对象编程02-02(封装、继承、多态)
封装、继承和多态是面向对象编程(OOP)的三大核心特性,理解它们对掌握Java至关重要。我会用通俗易懂的方式为你讲解,并结合代码示例帮助你理解。
一、封装(Encapsulation)
1. 什么是封装?
- 定义:将数据(属性)和操作数据的方法(行为)捆绑在一个类中,并对外隐藏内部实现细节。
- 核心思想:隐藏对象的属性和实现细节,仅对外公开接口(方法)进行交互。
- 目的:提高安全性、可维护性、灵活性(例如:避免属性被随意修改)。
2. 如何实现封装?
- 使用访问修饰符控制属性的可见性:
private
:属性只能在当前类内访问。public
:属性或方法可以被任何类访问。protected
:允许子类和同包类访问。
- 提供公共的getter和setter方法操作属性。
3. 代码示例
public class Student {// 私有属性,外部无法直接访问private String name;private int age;// 公共的getter方法,允许外部获取属性值public String getName() {return name;}// 公共的setter方法,允许外部设置属性值(可添加校验逻辑)public void setName(String name) {if (name != null && !name.isEmpty()) {this.name = name;}}public int getAge() {return age;}public void setAge(int age) {if (age >= 0 && age <= 120) { // 校验年龄合法性this.age = age;}}
}// 使用示例
public class Main {public static void main(String[] args) {Student stu = new Student();stu.setName("Alice");stu.setAge(20);System.out.println(stu.getName() + ": " + stu.getAge());}
}
4. 封装的好处
- 安全性:防止属性被非法修改(如年龄为负数)。
- 灵活性:可以在setter方法中添加校验逻辑,而外部调用者无需关心。
- 代码维护:修改类的内部实现时,不影响外部代码。
二、继承(Inheritance)
1. 什么是继承?
- 定义:子类(派生类)继承父类(基类)的属性和方法,并可以扩展自己的功能。
- 核心思想:代码复用 + 扩展功能。
- 语法:使用
extends
关键字。
2. 代码示例
// 父类:Animal
class Animal {private String name;public void eat() {System.out.println(name + " is eating.");}public void setName(String name) {this.name = name;}
}// 子类:Dog(继承Animal)
class Dog extends Animal {// 扩展自己的方法public void bark() {System.out.println("Woof! Woof!");}
}// 使用示例
public class Main {public static void main(String[] args) {Dog dog = new Dog();dog.setName("Buddy");dog.eat(); // 调用父类方法dog.bark(); // 调用子类方法}
}
3. 继承的注意事项
- 单继承:Java中一个类只能直接继承一个父类(不支持多继承)。
- 构造方法:子类构造方法默认会调用父类的无参构造方法(可通过
super()
显式调用父类构造方法)。 - 方法重写(Override):子类可以重写父类的方法(后面多态中详细讲解)。
三、多态(Polymorphism)
1. 什么是多态?
- 定义:同一操作作用于不同对象,可以有不同的行为(例如:同一方法在不同子类中有不同实现)。
- 两种形式:
- 编译时多态(静态多态):方法重载(Overload)。
- 运行时多态(动态多态):方法重写(Override) + 向上转型(Upcasting)。
2. 方法重写(Override)
- 子类重新定义父类中已有的方法。
- 规则:
- 方法名、参数列表必须与父类方法相同。
- 返回类型可以是父类方法返回类型的子类。
- 访问权限不能比父类方法更严格。
class Animal {public void makeSound() {System.out.println("Animal makes a sound");}
}class Dog extends Animal {@Override // 注解:表示重写父类方法public void makeSound() {System.out.println("Dog barks: Woof!");}
}class Cat extends Animal {@Overridepublic void makeSound() {System.out.println("Cat meows: Meow~");}
}
3. 向上转型(Upcasting)
- 将子类对象赋值给父类引用变量。
- 目的:通过父类引用调用子类重写的方法,实现多态。
public class Main {public static void main(String[] args) {Animal animal1 = new Dog(); // 向上转型Animal animal2 = new Cat();animal1.makeSound(); // 输出 Dog barks: Woof!animal2.makeSound(); // 输出 Cat meows: Meow~}
}
4. 多态的优势
- 代码扩展性:新增子类时无需修改原有代码。
- 统一接口:用父类引用处理不同子类对象。
四、总结与对比
特性 | 核心思想 | 关键实现方式 |
---|---|---|
封装 | 隐藏细节,暴露接口 | private 属性 + getter/setter |
继承 | 代码复用,扩展功能 | extends 关键字 + 方法重写 |
多态 | 同一方法不同表现 | 方法重写 + 向上转型 |
五、常见问题
1. 方法重载(Overload) vs 方法重写(Override)
- 重载:同一类中,方法名相同,参数列表不同。
- 重写:子类中重新实现父类方法。
2. 什么时候用继承?
- 符合 “is-a” 关系时(例如:Dog is a Animal)。
- 避免过度继承(优先考虑组合而非继承)。
通过这三个特性,你可以设计出灵活、可维护的Java程序。建议结合代码实践,尝试自己编写一些类,体会它们之间的关系!
JavaSE核心知识点02面向对象编程02-03(抽象类与接口)
作为Java面向对象编程的两个核心概念,抽象类和接口是代码设计中非常重要的工具。下面我会从基础概念到实际应用,用简单易懂的方式为你详细讲解它们的区别和使用场景。
一、抽象类(Abstract Class)
1. 什么是抽象类?
- 定义:用
abstract
关键字修饰的类,不能被实例化(不能直接new
)。 - 核心作用:为子类提供公共模板,强制子类实现某些方法。
- 特点:
- 可以包含普通方法(有方法体)和抽象方法(无方法体)。
- 可以有成员变量、构造方法、静态方法等。
- 单继承:一个类只能继承一个抽象类。
2. 抽象方法
- 定义:用
abstract
修饰的方法,没有方法体,必须由子类实现。public abstract class Animal {// 抽象方法:没有方法体public abstract void eat();// 普通方法public void sleep() {System.out.println("动物在睡觉");} }
3. 继承抽象类
- 子类必须实现所有抽象方法,否则子类也必须声明为
abstract
。public class Cat extends Animal {@Overridepublic void eat() {System.out.println("猫吃鱼");} }
4. 使用场景
- 代码复用:多个子类有共同的代码逻辑(如
sleep()
方法)。 - 强制规范:要求子类必须实现某些功能(如
eat()
方法)。
二、接口(Interface)
1. 什么是接口?
- 定义:用
interface
关键字定义,表示一组行为规范。 - 核心作用:定义类能做什么(能力),而不是是什么(类型)。
- 特点:
- 默认方法:Java 8+ 允许接口包含
default
方法(有方法体)。 - 静态方法:Java 8+ 允许接口包含
static
方法。 - 多实现:一个类可以实现多个接口。
- 没有构造方法,不能实例化。
- 成员变量默认是
public static final
(常量)。
- 默认方法:Java 8+ 允许接口包含
2. 定义接口
public interface Flyable {// 抽象方法(默认是 public abstract)void fly();// Java 8+ 默认方法default void land() {System.out.println("正在着陆");}// Java 8+ 静态方法static void checkAltitude() {System.out.println("检查飞行高度");}
}
3. 实现接口
- 使用
implements
关键字,必须实现所有抽象方法。public class Bird implements Flyable {@Overridepublic void fly() {System.out.println("鸟在飞");} }
4. 多实现
- 一个类可以实现多个接口,解决Java单继承的局限性。
public class SuperDrone implements Flyable, Chargeable {// 实现所有接口的抽象方法 }
5. 使用场景
- 定义行为:比如
Flyable
(可飞)、Swimmable
(可游泳)。 - 解耦代码:通过接口隔离不同模块的依赖。
- 多态性:通过接口类型引用对象,提高灵活性。
三、抽象类 vs 接口:核心区别
特性 | 抽象类 | 接口 |
---|---|---|
继承/实现 | 单继承(extends) | 多实现(implements) |
方法 | 可以有抽象方法和具体方法 | Java 8前只有抽象方法,现在可以有默认方法和静态方法 |
成员变量 | 可以是普通变量 | 默认是 public static final (常量) |
构造方法 | 有构造方法 | 无构造方法 |
设计目的 | 表示“是什么”(is-a关系) | 表示“能做什么”(has-a能力) |
使用场景 | 代码复用 + 部分规范 | 完全规范 + 行为扩展 |
四、如何选择抽象类 vs 接口?
1. 用抽象类的情况
- 多个子类有共同的属性和方法需要复用。
- 需要定义非 public 的成员或非静态方法。
- 需要定义构造方法初始化状态。
2. 用接口的情况
- 需要定义一组行为规范(如不同设备的功能)。
- 需要实现多继承(一个类实现多个接口)。
- 希望解耦代码,提高扩展性。
五、实际案例
场景:设计一个游戏中的角色系统
- 抽象类:
Character
(角色),定义公共属性(如生命值、位置)和方法(如移动)。 - 接口:
Attackable
(可攻击)、Defendable
(可防御),定义不同角色的能力。
// 抽象类:角色基类
public abstract class Character {protected int health;protected int x, y;public Character(int health) {this.health = health;}public abstract void move(); // 抽象方法:移动方式由子类实现public void takeDamage(int damage) {health -= damage;}
}// 接口:可攻击
public interface Attackable {void attack(Character target);
}// 接口:可防御
public interface Defendable {void defend();
}// 具体类:战士
public class Warrior extends Character implements Attackable, Defendable {public Warrior() {super(100);}@Overridepublic void move() {System.out.println("战士步行移动");}@Overridepublic void attack(Character target) {System.out.println("战士用剑攻击");target.takeDamage(20);}@Overridepublic void defend() {System.out.println("战士举盾防御");}
}
六、常见误区
- 滥用继承:如果不需要复用代码,优先用接口。
- 过度设计:不要为了用抽象类而用,根据需求选择。
- 忽略默认方法:Java 8+ 的接口可以通过
default
方法提供默认实现,减少代码重复。
七、总结
- 抽象类:是什么(is-a),适合代码复用 + 部分规范。
- 接口:能做什么(has-a),适合定义行为 + 多态扩展。
掌握两者的区别和适用场景,能帮助你写出更灵活、可维护的代码!如果还有疑问,可以尝试写几个小例子练习一下~ 😊
JavaSE核心知识点02面向对象编程02-04(包和导入)
包(Package)和导入(Import)是 Java 中组织和管理代码的核心机制,尤其适合项目规模较大时的代码维护。以下是专门整理的详细指南,从基础到实践,逐步掌握它们的使用方法。
一、包(Package)的作用
- 避免命名冲突
不同包中可以有同名的类,例如:com.company1.utils.StringUtil
和com.company2.utils.StringUtil
。 - 组织代码结构
按功能或模块分类类文件,例如:com.example.dao
(数据库操作)、com.example.model
(数据模型)。 - 控制访问权限
Java 的访问修饰符(如protected
、默认权限)与包密切相关。
二、定义包
-
语法
在 Java 文件的第一行使用package
声明包:package com.example.util; // 定义当前类属于 com.example.util 包
-
包命名规范
- 全部小写,使用公司域名的倒序(如
com.example
)。 - 多层包名用
.
分隔,对应文件系统的目录结构(如com/example/util
)。
- 全部小写,使用公司域名的倒序(如
-
示例
- 文件路径:
src/com/example/util/StringUtil.java
- 类定义:
package com.example.util; // 必须与目录结构一致!public class StringUtil {public static String trim(String str) {return str.trim();} }
- 文件路径:
三、导入(Import)其他包的类
-
为什么需要导入
使用其他包中的类时,需要通过import
引入,否则需写类的全限定名(Fully Qualified Name)。 -
导入单个类
import java.util.ArrayList; // 导入 ArrayListpublic class Main {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();} }
-
导入整个包
使用通配符*
导入包下所有类(不推荐,可能引发命名冲突):import java.util.*; // 导入 java.util 包下的所有类
-
静态导入(Static Import)
导入类的静态成员(方法或变量),可直接使用:import static java.lang.Math.PI; // 导入 Math 类的 PI 常量public class Circle {double area(double radius) {return PI * radius * radius; // 直接使用 PI,无需写 Math.PI} }
四、常见场景与注意事项
1. 同一包内的类无需导入
// 文件:com/example/Main.java
package com.example;public class Main {public static void main(String[] args) {User user = new User(); // User 类在同一个包 com.example 中,无需导入}
}// 文件:com/example/User.java
package com.example;public class User {// 类定义
}
2. 不同包中的类必须导入
// 文件:com/test/App.java
package com.test;import com.example.User; // 导入其他包的类public class App {public static void main(String[] args) {User user = new User();}
}
3. 处理同名类冲突
如果两个包中有同名类,需用全限定名或只导入其中一个:
import java.util.Date; // 导入 java.util.Datepublic class ConflictExample {public static void main(String[] args) {Date utilDate = new Date(); // java.util.Datejava.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis()); // 全限定名}
}
五、默认包(不推荐使用)
- 如果不写
package
语句,类会放在默认包(无名包)。 - 问题:默认包的类无法被其他包的类导入,且容易引发命名冲突。
- 建议:始终为类指定明确的包名!
六、包与访问权限
public
:所有包可见。protected
:同包或子类可见。- 默认(无修饰符):同包可见。
private
:仅本类可见。
package com.example;public class Demo {public void publicMethod() {} // 所有包可见protected void protectedMethod() {} // 同包或子类可见void defaultMethod() {} // 同包可见private void privateMethod() {} // 仅本类可见
}
七、实战练习
-
创建包和类
- 创建包
com.example.utils
,在其中定义工具类MathUtils
。 - 在另一个包
com.example.app
中创建Main
类,导入并使用MathUtils
。
- 创建包
-
代码示例
// 文件:com/example/utils/MathUtils.java package com.example.utils;public class MathUtils {public static int add(int a, int b) {return a + b;} }// 文件:com/example/app/Main.java package com.example.app;import com.example.utils.MathUtils; // 导入工具类public class Main {public static void main(String[] args) {int sum = MathUtils.add(3, 5);System.out.println("Sum: " + sum); // 输出 Sum: 8} }
八、常见问题解答
-
IDE(如 IntelliJ IDEA)如何帮助管理包?
- 自动生成包目录结构。
- 自动提示导入缺失的类。
- 优化导入(删除未使用的导入)。
-
导入会影响性能吗?
- 不会!
import
仅是编译时声明,不影响运行时性能。
- 不会!
-
为什么有时需要写全限定名?
- 当两个导入的类同名时(如
java.util.Date
和java.sql.Date
),必须用全限定名区分。
- 当两个导入的类同名时(如
掌握包和导入后,你的代码将更加结构化,团队协作和大型项目管理也会更轻松!遇到问题时,多动手写代码实践,理解会更深刻。
JavaSE核心知识点02面向对象编程02-05(方法)
作为 Java 初学者,理解**方法(Method)**是掌握编程的关键一步。方法是 Java 中封装代码逻辑的基本单元,类似于生活中的“工具”或“功能”。下面我会用通俗易懂的方式详细讲解,并附上代码示例。
一、方法是什么?
- 定义:方法是一段有名字的代码块,用于完成特定任务(如计算、数据处理等)。
- 类比:假设榨汁机是一个方法,你放入水果(输入参数),它榨汁(执行代码),返回果汁(返回值)。
- 核心作用:复用代码(避免重复写相同逻辑)、模块化(分解复杂问题)。
二、方法的组成
一个方法由以下部分组成(逐步拆解):
// 示例:计算两数之和的方法
public static int add(int a, int b) { // 方法头(声明)int sum = a + b; // 方法体(具体逻辑)return sum; // 返回值
}
-
修饰符(如
public static
):public
:方法可以被其他类访问。static
:静态方法,可通过类名直接调用(无需创建对象)。
-
返回类型(如
int
):- 方法执行后返回的数据类型(如
int
,String
,void
表示无返回值)。
- 方法执行后返回的数据类型(如
-
方法名(如
add
):- 遵循驼峰命名法(如
calculateSum
,printMessage
)。
- 遵循驼峰命名法(如
-
参数列表(如
(int a, int b)
):- 输入的数据,可以是零个或多个参数,用逗号分隔。
-
方法体(
{}
中的代码):- 实现具体功能的代码逻辑。
-
返回值(如
return sum
):- 必须与声明的返回类型匹配,若返回类型是
void
,则无需return
。
- 必须与声明的返回类型匹配,若返回类型是
三、如何定义方法?
1. 无参数、无返回值的方法
public static void sayHello() {System.out.println("Hello, Java!");
}
2. 带参数、有返回值的方法
// 检查是否为偶数
public static boolean isEven(int number) {return number % 2 == 0;
}
3. 多个参数的方法
// 计算三个数的最大值
public static int max(int a, int b, int c) {int maxValue = a;if (b > maxValue) maxValue = b;if (c > maxValue) maxValue = c;return maxValue;
}
四、如何调用方法?
1. 调用静态方法(static
修饰)
public class Main {public static void main(String[] args) {// 直接通过类名调用静态方法int result = add(3, 5); // 调用示例中的 add 方法System.out.println(result); // 输出 8}public static int add(int a, int b) {return a + b;}
}
2. 调用非静态方法(需要对象实例)
public class Calculator {// 非静态方法public int multiply(int a, int b) {return a * b;}public static void main(String[] args) {// 1. 创建对象Calculator calc = new Calculator();// 2. 通过对象调用方法int product = calc.multiply(4, 5);System.out.println(product); // 输出 20}
}
五、方法的重载(Overload)
- 定义:同一个类中,方法名相同,但参数列表不同(类型、数量、顺序)。
- 作用:提供多种处理方式,简化调用。
public class OverloadDemo {// 重载 add 方法public static int add(int a, int b) {return a + b;}public static double add(double a, double b) {return a + b;}public static int add(int a, int b, int c) {return a + b + c;}public static void main(String[] args) {System.out.println(add(2, 3)); // 调用 int 版本System.out.println(add(2.5, 3.5)); // 调用 double 版本System.out.println(add(1, 2, 3)); // 调用三个参数的版本}
}
六、递归方法(Recursion)
- 定义:方法内部调用自身。
- 关键:必须有一个终止条件,否则会无限递归导致栈溢出。
// 计算 n 的阶乘(n!)
public static int factorial(int n) {if (n == 0 || n == 1) {return 1; // 终止条件} else {return n * factorial(n - 1); // 递归调用}
}public static void main(String[] args) {System.out.println(factorial(5)); // 输出 120(5! = 5×4×3×2×1)
}
七、常见问题解答
1. 方法是否需要返回值?
- 如果方法需要返回结果,用
return
指定返回类型(如int
,String
)。 - 如果只是执行操作(如打印),返回类型设为
void
。
2. 参数传递是“值传递”还是“引用传递”?
- Java 是值传递:方法内修改基本类型参数的值不会影响原始变量;对象类型参数传递的是对象引用的副本(修改对象属性会影响原对象)。
3. 静态方法 vs 非静态方法?
- 静态方法:属于类,通过
类名.方法名()
调用。 - 非静态方法:属于对象,需先创建对象再调用。
八、总结与练习
- 核心要点:
- 方法是封装代码的工具。
- 通过参数接收输入,通过返回值输出结果。
- 学会方法重载和递归。
- 练习:
- 写一个方法,判断一个数是否为素数。
- 写一个方法,反转字符串(如输入 “hello”,返回 “olleh”)。
- 用递归实现斐波那契数列。
通过以上内容,你应该对 Java 方法有了全面的理解。如果遇到问题,可以随时问我!
JavaSE核心知识点02面向对象编程02-06(泛型)
泛型(Generics)是 Java 中非常重要的特性,它让代码更安全、更灵活。下面用通俗易懂的方式为你讲解,包含代码示例和核心概念。
一、为什么需要泛型?
问题:假设你要写一个可以存放任何类型数据的容器(比如一个盒子)。没有泛型时,代码可能这样写:
class Box {private Object data; // 用 Object 存储任意类型public void setData(Object data) {this.data = data;}public Object getData() {return data;}
}
缺陷:
- 取出数据时需要强制类型转换,容易出错。
- 无法限制放入的类型,可能混入不同类型的数据。
泛型的解决方案:让容器在定义时声明它能存储的数据类型。
Box<String> box = new Box<>(); // 只能存 String
box.setData("Hello"); // 正确
// box.setData(123); // 编译报错!
String data = box.getData(); // 无需强制转换
二、泛型的基本语法
1. 定义泛型类/接口
在类名后加<T>
,T
是类型参数(可以是任意标识符,如E
, K
, V
等)。
class Box<T> { // T 表示“某种类型”private T data;public void setData(T data) {this.data = data;}public T getData() {return data;}
}
2. 使用泛型类
创建对象时指定具体类型:
Box<String> stringBox = new Box<>(); // 存储 String
Box<Integer> intBox = new Box<>(); // 存储 Integer
三、泛型方法
即使类不是泛型,方法也可以独立声明泛型:
public class Utils {// 泛型方法:在返回类型前加 <T>public static <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}}
}// 使用
Integer[] intArray = {1, 2, 3};
Utils.printArray(intArray); // 自动推断类型为 Integer
四、泛型通配符 ?
用于处理未知类型,常见于方法参数或集合操作。
1. 无界通配符 <?>
表示接受任何类型:
public static void printList(List<?> list) {for (Object elem : list) {System.out.println(elem);}
}
2. 上界通配符 <? extends T>
表示类型是T
或其子类:
// 只能读取元素(因为元素是 Animal 或其子类)
public static void processAnimals(List<? extends Animal> animals) {for (Animal animal : animals) {animal.eat();}
}
3. 下界通配符 <? super T>
表示类型是T
或其父类:
// 可以写入元素(因为容器是 T 的父类)
public static void addNumbers(List<? super Integer> list) {list.add(123); // 允许添加 Integer
}
PECS 原则(Producer Extends, Consumer Super)
- 生产者(Producer):使用
<? extends T>
,只能读取。 - 消费者(Consumer):使用
<? super T>
,只能写入。
五、类型擦除(Type Erasure)
Java 泛型在编译后会被擦除为原始类型(如Object
),这是为了兼容旧版本 Java。例如:
List<String> list = new ArrayList<>();
// 编译后实际是:List list = new ArrayList();
影响:
- 运行时无法获取泛型的具体类型(如
T
的实际类型)。 - 不能创建泛型数组(如
new T[]
)。 - 泛型类型不能是基本类型(如
List<int>
错误,要用List<Integer>
)。
六、泛型的限制与注意事项
-
不能实例化类型参数:
// 错误!new T() 是非法的 T data = new T();
-
静态成员不能使用泛型类型:
class Box<T> {// 错误!静态变量不能是泛型private static T staticData; }
-
泛型与继承的关系:
List<String>
不是List<Object>
的子类。- 但
ArrayList<String>
是List<String>
的子类。
七、典型应用场景
- 集合框架(如
ArrayList<E>
、HashMap<K,V>
)。 - 工具类(如
Collections
中的泛型方法)。 - 自定义通用数据结构(如栈、队列、链表)。
八、代码示例:自定义泛型栈
class Stack<T> {private List<T> elements = new ArrayList<>();public void push(T element) {elements.add(element);}public T pop() {if (elements.isEmpty()) {throw new EmptyStackException();}return elements.remove(elements.size() - 1);}
}// 使用
Stack<Integer> stack = new Stack<>();
stack.push(1);
int num = stack.pop(); // 无需强制转换
九、总结
- 核心目的:提高代码的类型安全性和复用性。
- 关键语法:
<T>
定义泛型,?
处理未知类型。 - 注意事项:类型擦除、通配符的使用场景、PECS原则。
掌握泛型后,你将能写出更健壮、更灵活的 Java 代码!
JavaSE核心知识点02面向对象编程02-07(枚举)
以下是关于Java泛型的详细讲解,从基础概念到实际应用逐步展开:
一、泛型存在的意义
1.1 类型安全
// 泛型出现前的集合使用(JDK1.5之前)
List list = new ArrayList();
list.add("Hello");
list.add(100); // 编译通过,运行时可能抛出ClassCastExceptionString str = (String) list.get(1); // 运行时报错:Integer无法转String
1.2 代码复用
通过泛型实现通用容器:
// 泛型类定义
public class Box<T> {private T content;public void set(T content) { this.content = content; }public T get() { return content; }
}// 使用时指定具体类型
Box<String> stringBox = new Box<>();
Box<Integer> intBox = new Box<>();
二、泛型核心语法
2.1 泛型类
// 多类型参数示例
public class Pair<K, V> {private K key;private V value;public Pair(K key, V value) {this.key = key;this.value = value;}public K getKey() { return key; }public V getValue() { return value; }
}// 使用示例
Pair<String, Integer> pair = new Pair<>("age", 25);
2.2 泛型方法
// 独立于类的泛型方法
public class Utils {public static <T> void printArray(T[] array) {for (T element : array) {System.out.print(element + " ");}System.out.println();}
}// 调用示例
Integer[] intArr = {1, 2, 3};
Utils.<Integer>printArray(intArr); // 显式指定类型
Utils.printArray(new String[]{"A", "B"}); // 自动类型推断
2.3 泛型接口
// 泛型接口定义
public interface Generator<T> {T generate();
}// 实现类指定具体类型
class StringGenerator implements Generator<String> {@Overridepublic String generate() {return UUID.randomUUID().toString();}
}
三、类型通配符
3.1 上界通配符(<? extends T>)
// 接收Number及其子类集合
public static double sum(List<? extends Number> list) {double sum = 0;for (Number num : list) {sum += num.doubleValue();}return sum;
}// 可以传入List<Integer>或List<Double>
List<Integer> ints = Arrays.asList(1,2,3);
System.out.println(sum(ints)); // 输出6.0
3.2 下界通配符(<? super T>)
// 向集合中添加元素
public static void addNumbers(List<? super Integer> list) {for (int i = 1; i <= 5; i++) {list.add(i);}
}List<Number> numbers = new ArrayList<>();
addNumbers(numbers); // 合法操作
3.3 无界通配符(<?>)
// 仅需遍历集合元素时不关心类型
public static void printList(List<?> list) {for (Object elem : list) {System.out.print(elem + " ");}System.out.println();
}
四、类型擦除与限制
4.1 类型擦除原理
编译后泛型类型会被擦除为原始类型:
// 编译前
List<String> list = new ArrayList<>();// 编译后(字节码层面)
List list = new ArrayList();
4.2 泛型限制
- 无法实例化类型参数
public class Box<T> {// 错误写法private T instance = new T(); // 正确方式通过反射(需谨慎)public T createInstance(Class<T> clazz) throws Exception {return clazz.newInstance();}
}
- 无法使用基本类型
// List<int> list; // 编译错误
List<Integer> list; // 正确
- 静态成员不能使用类型参数
public class Box<T> {// static T staticField; // 错误
}
五、泛型高级应用
5.1 泛型与反射
public class GenericFactory<T> {private Class<T> type;public GenericFactory(Class<T> type) {this.type = type;}public T createInstance() throws Exception {return type.getDeclaredConstructor().newInstance();}
}// 使用示例
GenericFactory<StringBuilder> factory = new GenericFactory<>(StringBuilder.class);
StringBuilder sb = factory.createInstance();
5.2 泛型数组
// 正确创建泛型数组的方式
public class ArrayMaker<T> {private Class<T> kind;public ArrayMaker(Class<T> kind) {this.kind = kind;}@SuppressWarnings("unchecked")T[] create(int size) {return (T[]) Array.newInstance(kind, size);}
}// 使用示例
ArrayMaker<String> maker = new ArrayMaker<>(String.class);
String[] strArray = maker.create(10);
六、最佳实践与常见错误
6.1 PECS原则(Producer Extends, Consumer Super)
// 正确使用示例
public static <T> void copy(List<? super T> dest, List<? extends T> src) {for (T item : src) {dest.add(item);}
}List<Number> dest = new ArrayList<>();
List<Integer> src = Arrays.asList(1,2,3);
copy(dest, src); // 正确执行
6.2 常见错误示例
// 错误1:类型不匹配
List<String> list = new ArrayList<Object>(); // 编译错误// 错误2:原始类型警告
List rawList = new ArrayList<String>(); // 编译警告
rawList.add(100); // 运行时可能出错
七、学习建议
-
实践路线:
- 从集合框架(如ArrayList)开始体验泛型
- 尝试自定义泛型类(如实现一个通用缓存类)
- 研究JDK源码中的泛型应用(如Comparable接口)
-
调试技巧:
- 使用
javac -Xlint:unchecked
查看未检查警告 - 在IDE中观察类型擦除后的字节码
- 使用
-
延伸学习:
- 泛型与设计模式(如工厂模式、策略模式)
- Java8后的改进(如泛型类型推断增强)
- Kotlin/Scala等JVM语言的泛型特性对比
通过系统学习和大量编码实践,通常可在2-3周内掌握泛型的核心用法。理解难点时,建议通过绘制类型关系图辅助记忆(如通配符的继承关系)。
JavaSE核心知识点02面向对象编程02-8(异常处理)
好的!作为Java新手,掌握异常处理是编写健壮程序的关键。以下是一份详细的Java异常处理指南,包含基础概念、使用方法和实际示例:
一、异常处理的核心概念
1. 什么是异常?
- 异常(Exception):程序运行时发生的非正常情况,会中断正常的指令流。
- 异常处理的必要性:避免程序崩溃、提供友好错误提示、确保资源正确释放。
2. Java异常分类
Java异常体系以 Throwable
为根类,分为两类:
- Error:严重错误(如内存溢出
OutOfMemoryError
),程序无法处理。 - Exception:可处理的异常,分为两类:
- Checked Exception(检查型异常):编译时强制检查(如
IOException
)。 - Unchecked Exception(非检查型异常):运行时异常(如
NullPointerException
、ArithmeticException
)。
- Checked Exception(检查型异常):编译时强制检查(如
二、异常处理的关键字
1. try-catch-finally
try {// 可能抛出异常的代码int result = 10 / 0; // 抛出 ArithmeticException
} catch (ArithmeticException e) {// 捕获并处理异常System.out.println("除数不能为零!");
} finally {// 无论是否发生异常,都会执行的代码(常用于释放资源)System.out.println("执行finally块");
}
2. throws
声明方法可能抛出的异常,由调用者处理:
public void readFile() throws IOException {FileReader file = new FileReader("test.txt");// ...
}
3. throw
手动抛出异常对象:
if (age < 0) {throw new IllegalArgumentException("年龄不能为负数!");
}
三、常见异常类型与处理
1. 常见异常示例
异常类型 | 触发场景 | 处理建议 |
---|---|---|
NullPointerException | 调用 null 对象的方法或属性 | 检查对象是否为 null |
ArrayIndexOutOfBounds | 访问数组越界位置 | 检查数组索引范围 |
IOException | 文件读写失败(如文件不存在) | 使用 try-catch 或 throws |
NumberFormatException | 字符串转换为数字失败(如 "abc" ) | 校验输入是否为有效数字 |
2. 处理多个异常
try {FileInputStream fis = new FileInputStream("file.txt");int data = fis.read();
} catch (FileNotFoundException e) {System.out.println("文件未找到!");
} catch (IOException e) {System.out.println("读取文件失败!");
} catch (Exception e) {// 兜底处理(不建议滥用)System.out.println("未知错误!");
}
四、自定义异常
1. 创建自定义异常类
public class InsufficientBalanceException extends Exception {public InsufficientBalanceException(String message) {super(message);}
}
2. 使用自定义异常
public void withdraw(double amount) throws InsufficientBalanceException {if (amount > balance) {throw new InsufficientBalanceException("余额不足!");}balance -= amount;
}
五、最佳实践与常见陷阱
1. 最佳实践
- 精准捕获:避免捕获过于宽泛的异常(如直接
catch (Exception e)
)。 - 资源释放:使用
try-with-resources
(Java 7+)自动关闭资源:try (FileInputStream fis = new FileInputStream("file.txt")) {// 使用资源 } catch (IOException e) {e.printStackTrace(); }
- 记录异常:使用日志工具(如
log4j
)记录异常堆栈:catch (SQLException e) {logger.error("数据库操作失败", e); }
2. 常见陷阱
- 忽略异常:空
catch
块会隐藏问题。// 错误示例! try {// 可能出错的代码 } catch (Exception e) {// 空处理 }
- 在finally中return:可能导致异常被覆盖。
try {// ... } finally {return; // 不推荐! }
六、总结与练习
1. 关键点总结
- 分类:理解Checked vs Unchecked异常的区别。
- 处理方式:
try-catch
、throws
、throw
的适用场景。 - 资源管理:优先使用
try-with-resources
替代手动关闭。
2. 练习题目
- 编写代码读取文件内容,处理可能的
FileNotFoundException
。 - 实现一个除法方法,当除数为零时抛出
ArithmeticException
。 - 自定义一个
InvalidEmailException
,并在用户输入无效邮箱时抛出。
通过实际编码练习,逐步掌握异常处理的核心技巧!遇到问题时,可查阅 Java官方文档 或调试工具(如IDE的断点功能)。
📜文末寄语
- 🟠关注我,获取更多内容。
- 🟡技术动态、实战教程、问题解决方案等内容持续更新中。
- 🟢《全栈知识库》技术交流和分享社区,集结全栈各领域开发者,期待你的加入。
- 🔵加入开发者的《专属社群》,分享交流,技术之路不再孤独,一起变强。
- 🟣点击下方名片获取更多内容🍭🍭🍭👇