Java 面向对象从入门到精通:基础、API与ATM系统实战
本文专为零基础新手设计,从面向对象核心概念讲起,逐步过渡到常用API的使用,最终通过一个完整的ATM系统实战,帮助你掌握Java面向对象编程思想。所有代码均附带详细注释,确保新手能轻松理解。
一、面向对象基础(从概念到代码)
1. 什么是面向对象?
- 面向过程:关注"步骤"(如做蛋糕:打鸡蛋→拌面粉→烘烤)
- 面向对象:关注"对象"(如做蛋糕:找厨师→用烤箱→用模具,对象是厨师、烤箱、模具)
面向对象的核心思想:将复杂问题拆解为多个对象,通过对象间的交互解决问题。
2. 类与对象(核心概念)
- 类(Class):抽象的"模板",定义对象的属性(特征)和方法(行为)。比如"手机类"定义了手机有品牌、价格(属性),能打电话、发短信(方法)。
- 对象(Object):类的具体"实例"。比如"我的iPhone"是"手机类"的一个对象,品牌是Apple,价格是5999,能实际打电话。
(1)定义类(模板)
语法:
public class 类名 {
// 属性(成员变量):定义对象的特征
数据类型 属性名;
// 方法:定义对象的行为
方法返回值类型 方法名() {
// 方法体
}
}示例:定义一个"学生类"
// 学生类(模板)
public class Student {
// 属性(特征):姓名、年龄、学号
String name; // 姓名
int age; // 年龄
String id; // 学号
// 方法(行为):学习
public void study() {
// 方法中可以使用属性(this代表当前对象)
System.out.println(this.name + "正在学习,学号是" + this.id);
}
// 方法(行为):介绍自己
public void introduce() {
System.out.println("大家好,我叫" + name + ",今年" + age + "岁");
}
}(2)创建对象(实例化)
语法:类名 对象名 = new 类名();
通过对象名.属性或对象名.方法()使用对象。
示例:用"学生类"创建对象
public class TestStudent {
public static void main(String[] args) {
// 创建第一个学生对象(实例)
Student stu1 = new Student();
// 给对象的属性赋值
stu1.name = "张三";
stu1.age = 18;
stu1.id = "2023001";
// 调用对象的方法
stu1.study(); // 输出:张三正在学习,学号是2023001
stu1.introduce(); // 输出:大家好,我叫张三,今年18岁
// 创建第二个学生对象
Student stu2 = new Student();
stu2.name = "李四";
stu2.age = 19;
stu2.id = "2023002";
stu2.introduce(); // 输出:大家好,我叫李四,今年19岁
}
}新手注意:
- 类名首字母大写(如
Student),对象名首字母小写(如stu1),遵循驼峰命名法。 - 每个对象的属性是独立的,修改
stu1的name不会影响stu2。
3. 构造器(初始化对象的特殊方法)
创建对象时,new后面的类名()其实是在调用构造器(Constructor),用于初始化对象的属性。
(1)默认构造器
如果类中没有定义构造器,Java会自动生成一个无参数的默认构造器,如:
public class Student {
// 默认构造器(隐藏)
public Student() {
}
}(2)自定义构造器
我们可以手动定义构造器,实现属性的初始化(比如创建对象时直接给姓名和年龄赋值)。
语法:
public 类名(参数列表) {
// 初始化属性
}示例:给学生类添加构造器
public class Student {
String name;
int age;
String id;
// 无参构造器(手动定义后,默认构造器会消失)
public Student() {
}
// 有参构造器:创建对象时直接赋值姓名和年龄
public Student(String n, int a) {
name = n; // 将参数n赋值给属性name
age = a; // 将参数a赋值给属性age
}
// 有参构造器:创建对象时赋值所有属性
public Student(String n, int a, String i) {
name = n;
age = a;
id = i;
}
// 方法省略...
}使用自定义构造器创建对象:
public class TestConstructor {
public static void main(String[] args) {
// 调用无参构造器(需手动赋值)
Student stu1 = new Student();
stu1.name = "王五";
stu1.age = 20;
// 调用有参构造器(直接赋值)
Student stu2 = new Student("赵六", 21); // 姓名=赵六,年龄=21
Student stu3 = new Student("钱七", 22, "2023003"); // 全属性赋值
stu2.introduce(); // 输出:大家好,我叫赵六,今年21岁
}
}新手注意:
- 构造器名必须与类名完全相同,且没有返回值类型(连
void都不能有)。 - 定义有参构造器后,默认无参构造器会消失,如需使用需手动添加。
(3)this关键字的用法
- 区分成员变量(类中定义的属性)和局部变量(方法中的参数或变量)。
- 在构造器中调用其他构造器(
this(参数),必须放在第一行)。
示例:用this优化构造器
public class Student {
String name;
int age;
String id;
// 无参构造器
public Student() {
}
// 有参构造器1:给name和age赋值
public Student(String name, int age) {
// this.name指成员变量,name指参数
this.name = name;
this.age = age;
}
// 有参构造器2:调用构造器1,再给id赋值
public Student(String name, int age, String id) {
this(name, age); // 调用上面的构造器(必须在第一行)
this.id = id;
}
}4. 封装(面向对象三大特性之一)
封装:将对象的属性隐藏(用private修饰),只通过公共方法(getter/setter)访问和修改,保护数据安全。
(1)为什么需要封装?
如果属性直接暴露(用public),可能被随意修改导致数据错误(比如年龄设置为负数)。封装可以在方法中添加验证逻辑。
(2)封装的步骤
- 用
private修饰属性(私有属性,只能在类内部访问)。 - 提供
public的getter方法(获取属性值)和setter方法(设置属性值,可添加验证)。
示例:封装学生类的年龄属性
public class Student {
private String name; // 私有属性
private int age; // 私有属性(年龄不能为负数)
private String id;
// 无参构造器
public Student() {
}
// getter方法:获取name
public String getName() {
return name;
}
// setter方法:设置name
public void setName(String name) {
this.name = name;
}
// getter方法:获取age
public int getAge() {
return age;
}
// setter方法:设置age,添加验证(年龄不能小于0)
public void setAge(int age) {
if (age < 0) {
System.out.println("年龄不能为负数,已默认设为0");
this.age = 0; // 非法值时设为默认值
} else {
this.age = age;
}
}
// 其他方法省略...
}使用封装后的对象:
public class TestEncapsulation {
public static void main(String[] args) {
Student stu = new Student();
// 不能直接访问私有属性(编译报错):stu.age = -5;
stu.setName("孙八");
stu.setAge(-5); // 输出:年龄不能为负数,已默认设为0
System.out.println(stu.getName() + "的年龄是" + stu.getAge()); // 孙八的年龄是0
}
}新手总结:封装的核心是"私有属性,公共方法",通过方法控制属性的访问,保证数据合法性。
二、常用API(String与ArrayList)
API(Application Programming Interface)是Java提供的预定义类和方法,直接调用即可实现功能,无需重复开发。
1. String类(字符串处理)
String用于表示字符串(如"hello"),是Java中最常用的类之一。
(1)String的特点
- 不可变性:字符串创建后内容不可修改,修改会产生新的String对象。
- 直接赋值时,相同内容的字符串会共享内存(常量池)。
(2)常用方法(附代码示例)
| 方法 | 功能 | 示例 |
|---|---|---|
length() | 获取字符串长度 | "abc".length() → 3 |
charAt(int index) | 获取指定索引的字符(索引从0开始) | "hello".charAt(1) → 'e' |
equals(String s) | 比较字符串内容是否相等(区分大小写) | "abc".equals("ABC") → false |
equalsIgnoreCase(String s) | 比较内容(不区分大小写) | "abc".equalsIgnoreCase("ABC") → true |
indexOf(String s) | 查找子串首次出现的索引(没找到返回-1) | "abcde".indexOf("cd") → 2 |
substring(int start) | 从start索引截取到末尾 | "abcde".substring(2) → "cde" |
substring(int start, int end) | 截取[start, end)范围(含头不含尾) | "abcde".substring(1,3) → "bc" |
replace(char old, char new) | 替换字符 | "hello".replace('l','x') → "hexxo" |
split(String regex) | 按指定规则分割字符串为数组 | "a,b,c".split(",") → ["a","b","c"] |
trim() | 去除首尾空格 | " abc ".trim() → "abc" |
示例代码:
public class StringDemo {
public static void main(String[] args) {
String s = "Hello, Java!";
// 获取长度
System.out.println("长度:" + s.length()); // 输出:11
// 获取索引1的字符
System.out.println("索引1的字符:" + s.charAt(1)); // 输出:e
// 截取子串(从7到末尾)
String sub = s.substring(7);
System.out.println("截取结果:" + sub); // 输出:Java!
// 替换字符
String replaced = s.replace('J', 'j');
System.out.println("替换后:" + replaced); // 输出:Hello, java!
// 分割字符串(按","分割)
String s2 = "apple,banana,orange";
String[] fruits = s2.split(",");
for (String fruit : fruits) {
System.out.println(fruit); // 依次输出apple、banana、orange
}
}
}新手注意:
- 比较字符串内容必须用
equals(),不能用==(==比较的是内存地址)。 - 不可变性示例:
s = s + "!"会创建新对象,原s的内容不变。
2. ArrayList(动态数组)
数组的长度固定,ArrayList是长度可变的"动态数组",可随时添加或删除元素,适合存储不确定数量的数据。
(1)创建ArrayList
需导入java.util.ArrayList,语法:
// 存储String类型的ArrayList
ArrayList<String> list = new ArrayList<>();
// 存储Integer(整数)类型的ArrayList
ArrayList<Integer> numbers = new ArrayList<>();<String>是泛型,指定集合中只能存储的类型(新手可理解为"元素类型")。
(2)常用方法(附代码示例)
| 方法 | 功能 | 示例 |
|---|---|---|
add(E e) | 添加元素到末尾 | list.add("苹果") |
add(int index, E e) | 在指定索引插入元素 | list.add(1, "香蕉") |
get(int index) | 获取指定索引的元素 | list.get(0) |
remove(int index) | 删除指定索引的元素 | list.remove(1) |
remove(Object o) | 删除指定元素 | list.remove("苹果") |
size() | 获取元素个数 | list.size() |
set(int index, E e) | 修改指定索引的元素 | list.set(0, "西瓜") |
clear() | 清空所有元素 | list.clear() |
contains(Object o) | 判断是否包含某元素 | list.contains("香蕉") |
示例代码:
import java.util.ArrayList; // 必须导入
public class ArrayListDemo {
public static void main(String[] args) {
// 创建存储字符串的ArrayList
ArrayList<String> fruits = new ArrayList<>();
// 添加元素
fruits.add("苹果");
fruits.add("香蕉");
fruits.add("橙子");
System.out.println("添加后:" + fruits); // 输出:[苹果, 香蕉, 橙子]
// 获取元素(索引0)
String first = fruits.get(0);
System.out.println("第一个元素:" + first); // 输出:苹果
// 修改元素(索引1改为"葡萄")
fruits.set(1, "葡萄");
System.out.println("修改后:" + fruits); // 输出:[苹果, 葡萄, 橙子]
// 删除元素(索引2)
fruits.remove(2);
System.out.println("删除后:" + fruits); // 输出:[苹果, 葡萄]
// 遍历元素(用for循环)
System.out.println("遍历元素:");
for (int i = 0; i < fruits.size(); i++) {
System.out.println(fruits.get(i)); // 依次输出苹果、葡萄
}
}
}新手注意:
ArrayList的索引也是从0开始,越界会报错。- 泛型只能是引用类型(如
String、Integer),不能是基本类型(如int,需用Integer替代)。
三、综合项目实战:ATM系统
1. 需求分析
模拟ATM机的基本功能:
- 用户登录(账号+密码)
- 查询余额
- 存款
- 取款
- 转账
- 退出系统
2. 系统设计(类结构)
- Account类:封装账户信息(卡号、密码、余额、姓名)。
- ATMSystem类:实现ATM的核心功能(登录、查询、存取款等)。
- Test类:主程序入口,启动ATM系统。
3. 代码实现(分步讲解)
(1)Account类(账户信息封装)
public class Account {
// 私有属性(封装)
private String cardId; // 卡号
private String password; // 密码
private double money; // 余额
private String name; // 姓名
// 无参构造器
public Account() {
}
// 有参构造器(创建账户时初始化信息)
public Account(String cardId, String password, double money, String name) {
this.cardId = cardId;
this.password = password;
this.money = money;
this.name = name;
}
// getter和setter方法(访问和修改私有属性)
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}(2)ATMSystem类(核心功能实现)
import java.util.ArrayList;
import java.util.Scanner;
public class ATMSystem {
// 用ArrayList存储所有账户(系统初始化时添加测试账户)
private ArrayList<Account> accounts = new ArrayList<>();
private Scanner sc = new Scanner(System.in); // 用于输入
private Account loginAccount; // 记录当前登录的账户
// 系统初始化:添加测试账户
public void init() {
Account acc1 = new Account("1001", "123456", 10000, "张三");
Account acc2 = new Account("1002", "654321", 5000, "李四");
accounts.add(acc1);
accounts.add(acc2);
}
// 启动ATM系统
public void start() {
init(); // 初始化账户
while (true) {
System.out.println("===欢迎使用ATM系统===");
System.out.println("1. 登录");
System.out.println("2. 退出");
System.out.print("请选择:");
int choice = sc.nextInt();
switch (choice) {
case 1:
login(); // 登录
break;
case 2:
System.out.println("谢谢使用,再见!");
return; // 退出程序
default:
System.out.println("输入错误,请重新选择!");
}
}
}
// 登录功能
private void login() {
System.out.println("===登录===");
if (accounts.size() == 0) {
System.out.println("系统中无账户,请先开户(本系统暂不支持开户,已预设测试账户)");
return;
}
// 输入卡号
System.out.print("请输入卡号:");
String cardId = sc.next();
// 查找账户
Account acc = findAccount(cardId);
if (acc == null) {
System.out.println("卡号不存在!");
return;
}
// 验证密码(最多输3次)
for (int i = 0; i < 3; i++) {
System.out.print("请输入密码:");
String pwd = sc.next();
if (acc.getPassword().equals(pwd)) {
System.out.println("登录成功!欢迎您," + acc.getName() + "!");
loginAccount = acc; // 记录当前登录账户
showMainMenu(); // 进入主菜单
return;
} else {
if (i == 2) {
System.out.println("密码错误3次,登录失败!");
return;
}
System.out.println("密码错误,还有" + (2 - i) + "次机会");
}
}
}
// 主菜单(登录后显示)
private void showMainMenu() {
while (true) {
System.out.println("===主菜单===");
System.out.println("1. 查询余额");
System.out.println("2. 存款");
System.out.println("3. 取款");
System.out.println("4. 转账");
System.out.println("5. 退出登录");
System.out.print("请选择:");
int choice = sc.nextInt();
switch (choice) {
case 1:
queryBalance();
break;
case 2:
deposit();
break;
case 3:
withdraw();
break;
case 4:
transfer();
break;
case 5:
System.out.println("已退出登录");
loginAccount = null; // 清空登录状态
return;
default:
System.out.println("输入错误,请重新选择!");
}
}
}
// 查询余额
private void queryBalance() {
System.out.println("===查询余额===");
System.out.println("您的余额为:" + loginAccount.getMoney() + "元");
}
// 存款
private void deposit() {
System.out.println("===存款===");
System.out.print("请输入存款金额:");
double money = sc.nextDouble();
if (money <= 0) {
System.out.println("存款金额必须大于0!");
return;
}
// 更新余额(原有余额+存款金额)
loginAccount.setMoney(loginAccount.getMoney() + money);
System.out.println("存款成功,当前余额:" + loginAccount.getMoney() + "元");
}
// 取款
private void withdraw() {
System.out.println("===取款===");
double balance = loginAccount.getMoney();
System.out.print("请输入取款金额:");
double money = sc.nextDouble();
// 验证金额
if (money <= 0) {
System.out.println("取款金额必须大于0!");
return;
}
if (money > balance) {
System.out.println("余额不足,当前余额:" + balance + "元");
return;
}
// 更新余额(原有余额-取款金额)
loginAccount.setMoney(balance - money);
System.out.println("取款成功,当前余额:" + loginAccount.getMoney() + "元");
}
// 转账
private void transfer() {
System.out.println("===转账===");
if (accounts.size() < 2) {
System.out.println("系统中账户不足,无法转账");
return;
}
// 输入目标卡号
System.out.print("请输入目标卡号:");
String targetCardId = sc.next();
// 不能转给自己
if (targetCardId.equals(loginAccount.getCardId())) {
System.out.println("不能向自己转账!");
return;
}
// 查找目标账户
Account targetAccount = findAccount(targetCardId);
if (targetAccount == null) {
System.out.println("目标卡号不存在!");
return;
}
// 验证目标账户姓名(防止输错卡号)
System.out.print("请输入目标账户姓名:");
String name = sc.next();
if (!name.equals(targetAccount.getName())) {
System.out.println("目标账户姓名错误!");
return;
}
// 输入转账金额
System.out.print("请输入转账金额:");
double money = sc.nextDouble();
double balance = loginAccount.getMoney();
// 验证金额
if (money <= 0) {
System.out.println("转账金额必须大于0!");
return;
}
if (money > balance) {
System.out.println("余额不足,当前余额:" + balance + "元");
return;
}
// 执行转账(当前账户减钱,目标账户加钱)
loginAccount.setMoney(balance - money);
targetAccount.setMoney(targetAccount.getMoney() + money);
System.out.println("转账成功,向" + targetAccount.getName() + "转账" + money + "元,当前余额:" + loginAccount.getMoney() + "元");
}
// 工具方法:根据卡号查找账户
private Account findAccount(String cardId) {
for (Account acc : accounts) {
if (acc.getCardId().equals(cardId)) {
return acc; // 找到返回账户
}
}
return null; // 没找到返回null
}
}(3)Test类(启动程序)
public class Test {
public static void main(String[] args) {
// 创建ATM系统并启动
ATMSystem atm = new ATMSystem();
atm.start();
}
}4. 系统说明与使用流程
测试账户:系统初始化了两个账户:
- 卡号1001,密码123456,余额10000元,姓名张三
- 卡号1002,密码654321,余额5000元,姓名李四
使用步骤:
- 运行Test类启动系统,选择"1. 登录"
- 输入卡号和密码(如1001和123456)
- 登录后可进行查询、存款、取款、转账等操作
- 转账时可向1002账户转账(需输入目标姓名"李四")
四、从入门到精通的总结
面向对象基础:
- 类是模板,对象是实例,通过
new创建对象。 - 构造器用于初始化对象,
this区分成员变量和局部变量。 - 封装通过
private和getter/setter保护数据,是面向对象的核心思想。
- 类是模板,对象是实例,通过
常用API:
String处理字符串,注意equals()比较内容,==比较地址。ArrayList是动态数组,适合存储可变数量的数据,掌握add、get、remove等方法。
项目实战:
- ATM系统综合运用了类、对象、封装、ArrayList等知识,通过面向对象的方式拆分功能,使代码更清晰易维护。
- 新手可尝试扩展功能(如修改密码、查询明细),加深对面向对象的理解。