Java面向对象入门:从C语言到类和对象
Java面向对象入门:从C语言到类和对象
1. 引入:为什么需要面向对象?
1.1 C语言写代码的痛点
假设你要写一个学生管理系统,需要存储学生的姓名、年龄、分数:
// C语言的方式charname[50];intage;intscore;// 如果要存多个学生,需要多个数组charnames[100][50];intages[100];intscores[100];// 添加一个学生,要同时操作三个数组voidaddStudent(char*name,intage,intscore){// 维护三个数组,很容易出错}问题:数据和操作数据的函数是分离的,代码一多就容易乱。
1.2 面向对象的解决方案
把相关的数据和操作打包在一起,形成一个整体:
// Java的方式:一个Student类搞定classStudent{Stringname;intage;intscore;}这就是面向对象的核心理念:把数据和对数据的操作封装在一起。
2. 类和对象:从图纸到房子
2.1 类是什么?
类是一张蓝图,描述了某类事物的共同特征。
比如"学生"这类事物:
- 有什么属性:姓名、年龄、分数
- 有什么行为:学习、考试、休息
classStudent{// 属性(成员变量)Stringname;intage;intscore;// 行为(成员方法)voidstudy(){System.out.println(name+"正在学习");}}2.2 对象是什么?
对象是根据类这张蓝图创建出来的具体实例。
就像根据一张图纸可以盖很多房子:
// 创建对象Students1=newStudent();s1.name="张三";s1.age=18;s1.study();// 输出:张三正在学习Students2=newStudent();s2.name="李四";s2.age=19;s2.study();// 输出:李四正在学习类和对象的关系:
- 类是抽象的、概念的(学生)
- 对象是具体的、实际的(张三、李四)
3. 封装:为什么要加private?
3.1 没有封装的问题
直接暴露属性会出问题:
Students=newStudent();s.age=-5;// 年龄怎么能是负数?但没有报错3.2 用private保护数据
classStudent{privateStringname;privateintage;// 私有:只有本类能访问privateintscore;// 通过getter获取publicintgetAge(){returnage;}// 通过setter设置(可以加逻辑判断)publicvoidsetAge(intage){if(age>=0&&age<=150){this.age=age;}else{System.out.println("年龄不合法");}}}封装的三个层次:
- 属性私有化:用
private修饰 - 提供getter/setter:控制访问方式
- 在setter中添加逻辑:保护数据合法性
3.3 对比C语言
| 维度 | C语言 | Java封装 |
|---|---|---|
| 数据保护 | 全靠程序员自觉 | 编译器强制检查 |
| 修改数据 | 直接改结构体成员 | 必须通过setter |
| 添加逻辑 | 要改所有使用的地方 | 只改setter方法 |
4. this关键字:谁调用我,我就是谁
4.1 什么时候用this?
当成员变量和局部变量名字冲突时:
classStudent{privateintage;// 成员变量publicvoidsetAge(intage){// 局部变量也叫ageage=age;// 问题:到底是谁赋值给谁?}}上面的代码分不清哪个是成员变量,哪个是参数。用this解决:
publicvoidsetAge(intage){this.age=age;// this.age是成员变量,age是参数}4.2 this的内存原理
this代表当前正在调用方法的那个对象:
Students1=newStudent();s1.setAge(18);// 执行setAge时,this就是s1Students2=newStudent();s2.setAge(20);// 执行setAge时,this就是s24.3 this的三种用法
| 用法 | 示例 | 说明 |
|---|---|---|
| 区分成员和局部 | this.age = age | 最常用 |
| 调用本类构造方法 | this(参数) | 必须在第一行 |
| 返回当前对象 | return this | 链式调用 |
5. 构造方法:创建对象时自动执行
5.1 什么是构造方法?
构造方法是创建对象时自动调用的特殊方法:
classStudent{privateStringname;privateintage;// 无参构造publicStudent(){System.out.println("对象被创建了");}// 有参构造publicStudent(Stringname,intage){this.name=name;this.age=age;}}5.2 构造方法的特点
| 特点 | 说明 |
|---|---|
| 方法名必须和类名相同 | 写错了就是普通方法 |
| 没有返回值类型 | 连void都不能写 |
| 自动调用 | new的时候自动执行 |
| 可以重载 | 可以写多个构造方法 |
5.3 注意事项
// 写了有参构造,最好手动写无参构造classStudent{publicStudent(Stringname){// 有参构造}// 如果不写无参构造,new Student()会报错}原因:如果写了有参构造,系统就不会再提供默认的无参构造了。
6. JavaBean:一种规范
JavaBean就是按照一定规范编写的类:
6.1 三条规范
- 成员变量私有化:用
private - 提供无参构造方法:方便框架反射创建对象
- 提供getter和setter:统一访问方式
6.2 标准写法
publicclassStudent{// 1. 私有成员变量privateStringname;privateintage;// 2. 无参构造publicStudent(){}// 3. 有参构造(可选,但建议写上)publicStudent(Stringname,intage){this.name=name;this.age=age;}// 4. getter/setterpublicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}}6.3 IDEA快捷生成
不用手写,用快捷键:
- Mac:
Command + N - Windows:
Alt + Insert
选择Getter and Setter、Constructor即可自动生成。
7. 内存图:理解栈和堆
7.1 Java的内存分区
| 区域 | 存储内容 | 特点 |
|---|---|---|
| 栈 | 局部变量、方法调用 | 方法执行完就释放 |
| 堆 | 对象、成员变量 | GC自动回收 |
| 方法区 | 类信息、静态变量 | 类加载时存入 |
7.2 基本数据类型 vs 引用数据类型
inta=10;// 基本类型:值直接存在栈里Students=newStudent();// 引用类型:栈里存地址,对象在堆里图解:
栈 堆 ┌─────┐ ┌─────────────┐ │ a=10│ │ Student对象 │ ├─────┤ │ name=null │ │ s │──────────────→│ age=0 │ └─────┘ └─────────────┘7.3 三种情况的对象内存图
情况1:一个对象
Students=newStudent();// 栈:s存地址 → 堆:Student对象情况2:两个对象
Students1=newStudent();Students2=newStudent();// 栈:s1和s2存不同地址 → 堆:两个独立对象情况3:两个引用指向同一个对象
Students1=newStudent();Students2=s1;// s2和s1指向同一个对象// 修改s1的属性,s2也会变8. 成员变量 vs 局部变量
| 维度 | 成员变量 | 局部变量 |
|---|---|---|
| 定义位置 | 类中、方法外 | 方法内或参数 |
| 作用域 | 整个类 | 方法内 |
| 默认值 | 有(int=0,引用=null) | 没有,必须初始化 |
| 内存位置 | 堆(对象里) | 栈 |
| 生命周期 | 随对象创建和销毁 | 随方法调用和结束 |
classStudent{privateintage;// 成员变量:有默认值0publicvoidstudy(){inthour;// 局部变量:没有默认值,必须先赋值hour=2;// 必须赋值才能用System.out.println("学了"+hour+"小时");}}9. 综合训练:文字版格斗游戏
9.1 需求
创建两个角色,互相攻击,直到一方血量归零。
9.2 代码实现
publicclassCharacter{privateStringname;//属性privateintblood;publicCharacter(){}//构造方法publicCharacter(Stringname,intblood){this.name=name;this.blood=blood;}publicvoidsetName(Stringname){//set,get 函数this.name=name;}publicStringgetName(){returnname;}publicintgetBlood(){returnblood;}publicvoidsetBlood(intblood){this.blood=blood;}publicvoidattack(Characterch1){//行为Randomr=newRandom();intattack=r.nextInt(20)+1;intbloodLive=ch1.getBlood()-attack;if(bloodLive<0){bloodLive=0;}ch1.setBlood(bloodLive);System.out.println(this.name+"打了"+ch1.name+attack+"点血量,"+ch1.name+"还剩"+bloodLive+"血量");}publicvoiddied(){System.out.println(this.getName()+"被打死了.");}}importjava.util.Scanner;publicclassdraft{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);Characterch1=newCharacter();Characterch2=newCharacter();System.out.println("输入两个挑战者的名字:");ch1.setName(sc.nextLine());ch2.setName(sc.nextLine());System.out.println("输入两个挑战者的血量:");ch1.setBlood(sc.nextInt());ch2.setBlood(sc.nextInt());while(ch1.getBlood()>0&&ch2.getBlood()>0){ch1.attack(ch2);if(ch2.getBlood()<=0){ch2.died();break;}ch2.attack(ch1);if(ch1.getBlood()<=0){ch1.died();break;}}}}10. 对象数组练习
10.1 基本对象数组
// 创建对象数组Student[]students=newStudent[3];// 给每个位置赋值students[0]=newStudent("张三",18);students[1]=newStudent("李四",19);students[2]=newStudent("王五",20);// 遍历for(inti=0;i<students.length;i++){System.out.println(students[i].getName());}10.2 操作(添加、删除、统计)
// 统计年龄大于18的学生intcount=0;for(Students:students){if(s.getAge()>18){count++;}}// 删除指定位置(把后面的往前移)for(inti=index;i<size-1;i++){students[i]=students[i+1];}students[size-1]=null;size--;11. 总结对比:C vs Java
| 维度 | C语言 | Java |
|---|---|---|
| 组织方式 | 函数 + 结构体 | 类(数据+方法打包) |
| 数据保护 | 无 | private + getter/setter |
| 内存管理 | malloc/free手动 | new + 自动GC |
| 构造初始化 | 手动写init函数 | 构造方法自动调用 |
| 数组长度 | 需自己维护 | arr.length |
