- 面向对象编程大家可以理解为是写程序的套路
- 我们先建一个包来学习下面的内容
一、面向对象编程快速入门
案例:打印出学生的总成绩、平均成绩
定义方法来解决(面向过程编程):
- 先建一个 Test 类,然后编写下面的程序:
package com.fenghuai.oop;
public class Test {
public static void main(String[] args) {
printTotalScore("张三", 100, 100);
printAverageScore("张三", 100, 100);
printTotalScore("李四", 59, 99);
printAverageScore("李四", 59, 99);
}
public static void printTotalScore(String name, double chinese, double math) {
System.out.println(name + "同学的总成绩是:" + (chinese + math));
}
public static void printAverageScore(String name, double chinese, double math) {
System.out.println(name + "同学的平均成绩是:" + (chinese + math) / 2);
}
}
定义对象来解决(面向对象编程):
- 先建一个 Student 类:
package com.fenghuai.oop;
public class Student {
String name;// 姓名
double chinese;// 语文成绩
double math;// 数学成绩
public void printTotalScore() {
System.out.println(name + "同学的总成绩是:" + (chinese + math));
}
public void printAverageScore() {
System.out.println(name + "同学的平均成绩是:" + (chinese + math) / 2);
}
}
- 再建一个测试类:
package com.fenghuai.oop;
public class Test1 {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "张三";
s1.chinese = 100;
s1.math = 100;
s1.printTotalScore();
s1.printAverageScore();
Student s2 = new Student();
s2.name = "李四";
s2.chinese = 59;
s2.math = 99;
s2.printTotalScore();
s2.printAverageScore();
}
}
面向过程编程和面向对象编程的区别:
- 面向过程编程:开发一个一个的方法,有数据要处理了,我们就调用方法来处理。
- 面向对象编程:开发一个一个的对象来处理数据,把数据交给对象,再调用对象的方法来完成对数据的处理。
- 大家可能会觉得:这种面向对象编程有点麻烦啊。那么我们接下来仔细讲解一下这种编程套路是怎么出来的?为什么要用这种套路?对象又该怎么理解?
二、深刻认识面向对象
1、面向对象编程的好处?
- 汽车的数据,找汽车对象处理;手机的数据,找手机对象处理;学生的数据,找学生对象处理等等等等…
- 符合人类思维习惯,编程更简单、更直观
2、程序中的对象是什么?如何得到?
- 对象本质上是一种特殊的数据结构
- ==对象是用类 new 出来的,有了类就可以创建出对象。
- 我自己的理解是:定义了一个对象,就相当于在一个表里面定义了这个对象的属性和方法。
3、对象是怎么出来的?
- class 也就是类,也称为对象的设计图(或者对象的模板,模板里面包括了对象的属性和方法等等)
- 这里我们定义了一个学生类 Student,也就是我们定义了一个对象 Student,这个对象的属性有姓名、语文成绩、数学成绩等。
三、多个对象在计算机中的执行原理
- 对象在计算机中的执行原理是怎么回事?
先 new 一个对象出来,每次 new Student() ,就是在堆内存中开辟一块内存区域代表一个学生对象。s1 和 s2 变量里面记住的是学生对象的地址。 - 接下来我讲一下上面的执行原理
(纯是博主以自己目前的学识理解的,不一定正确!!仅供参考,有真正的大佬可以随时和博主讨论,博主也学习一下。)
①运行 test 测试类,运行主方法
②在主方法中新建了一个学生对象 s1,调用学生类中的建对象的方法填入数据
③再通过类的地址将数据传入到主方法中
④在主方法中新建了一个学生对象 s2,调用学生类中的建对象的方法填入数据
⑤再通过类的地址将数据传入到主方法中 - 拓展:如何识别引用类型的变量?
Student s1 = new Student();
s1 变量中存储的是对象的地址,因此变量 s1 也称为引用类型的变量
四、类和对象的一些注意事项
- 类名建议用英文单词,首字母大写,满足驼峰模式,且要有意义,比如:Student、Car…
- 类中定义的变量也称为成员变量(对象的属性),类中定义的方法也称为成员方法(对象的行为)。
- 成员变量本身存在默认值,在定义成员变量时,不需要赋初始值(没有意义)
- 一个代码文件中,可以写多个 class 类,但只能一个用 public 修饰,且 public 修饰的类名必须成为代码文件名。
- 对象与对象之间的数据不会相互影响,但多个变量指向同一个对象时就会相互影响了,因为多个变量指向同一个对象时,使用的是同一个地址,所以同一个地址里面的内容更改了,所有相关变量都会更改。
- 如果某个对象没有一个变量引用它,则该对象无法被操作了,该对象会成为所谓的垃圾对象。Java 中存在自动垃圾回收机制,会自动清楚垃圾对象,程序员不用操心。
五、this
1、this 概述
- this 就是一个变量,可以用在方法中,来拿到当前的对象。例如可以像this.name、this.age、println(this)这样使用。
2、this 执行原理
- 需要分清成员变量和局部变量,成员变量是在方法外面定义的变量,而局部变量是在方法里面定义的变量。一般我们直接在方法内部使用变量时因为就近原则,一般使用的都是局部变量;而当我们输入this.age使用的就是成员变量了。(举例在下面)
- 就近原则:当方法内外的变量名称一致时,成员变量和局部变量,谁离我近,我就用谁。
3、this 应用场景
- this 主要用来解决变量名称冲突问题,也就是成员变量与方法内部的局部变量的名称一样时,导致访问冲突的问题。
4、this 和 就近原则的举例
- 这里我们新建了一个包来存放GirFriend和GirFriendTest,在测试类中新建一个对象,并调用对象中的方法。这里我们可以看到输出,第一个输出为 10,第二个输出为 0。是因为我们第一个输出使用了就近原则,而最近的方法中重新定义了变量的值,所以我们就输出了方法中的值;第二个输出使用了 this,所以使用的就是全局变量。
六、构造器
1、构造方法概述
- 构造方法也叫做构造器、构造函数。
- 作用:在创建对象的时候给成员变量进行赋值。
public class StudentDemo {
public static void main(String[] args) {
Student s1 = new Student();//创建对象
}
}
2、构造方法的格式
public class Student {
修饰符 类名(参数) {
方法体;
}
}
- 下面举例写一下空参构造和带参构造
public class Student {
private String name;
private int age;
public Student() {
//空参构造
}
public Student(String name, int age){
//带参构造
}
}
3、特点
- 方法名与类名相同,大小写也要一致
- 没有返回值类型,连 void 都没有
- 没有具体的返回值(不能由 return 带回结果数据)
4、执行时机
- 创建对象的时候由虚拟机调用,不能手动调用构造方法
- 每创建一次对象,就会调用一次构造方法
5、构造方法注意事项
①构造方法的定义
- 如果没有定义构造方法,系统将给出一个默认的无参数构造方法
- 如果定义了构造方法,系统将不再提供默认的构造方法
②构造方法的重载
- 带参构造方法,和无参数构造方法,两这方法名相同,但是参数不同,这叫做构造方法的重载
③推荐的使用方式
- 无论是否使用,都手动书写无参数构造方法,和带全部参数的构造方法
七、封装
1、Java 封装
封装介绍:
- 在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义
的代码随机访问。 - 要访问该类的代码和数据,必须通过严格的接口控制。
- 封装最主要的功能在于我们能修改自己的实现代码,而不用修改哪些调用我们代码的程序片段。
- 适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点:
- 良好的封装能够减少耦合
- 类内部的结构可以自由修改
- 可以对成员变量进行更精确的控制
- 隐藏信息,实现细节
封装的设计规范:
- 合理隐藏、合理暴露
代码层面如何控制对象的成员公开或隐藏?
- 公开成员,可以使用 public(公开)进行修饰
- 隐藏成员,使用 private(私有,隐藏)进行修饰
2、实现 Java 封装的步骤
- 修改属性的可见性来限制对属性的访问(一般限制为 private),对属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
- 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问。
- 采用 this 关键字是为了解决实例变量和局部变量之间发生的同名的冲突
3、变量名称冲突问题解决举例
- 上面我们了解了 this、就近原则、构造方法和封装,那么我们可以尝试解决一下变量名称冲突的问题了。(这里我们说明一下为什么要设置一样的变量名称,因为见名知意是我们定义变量名字的时候很重要的一点,我们在真正的开发时,不可能一个人负责整个项目,总会出现多个人负责同一个项目的情况,所以为了在项目中让大家更能理解变量的作用,我们一般都是使用见名知意的方式,总不能每次新定义一个变量的时候都要仔细记到一个表格中,每次使用前都得查阅吧,这样效率太低了,所以为了解决这个问题,所以就又出现了变量名称冲突的问题)
对象类:
- 首先我们先创建一个 test 包去建两个类,依旧是 GirlFriend 和 GirlFriendTest 两个类。
- 首先先在 GirlFriend 类中设置属性并设置相关构造方法,先给大家看一个手写的 name 的构造方法。
- 然后教大家简单生成构造方法的方式:点击右键,点击Generate,然后再点击Getter and Setter,然后再按住Ctrl键,点击两个变量,然后再点击OK,然后我们就可以看到自动生成的构造方法啦。
- 然后我们在各个构造方法里面修改相应的内容,修改后的对象类为:
package com.fenghuai.oop.test3;
public class GirlFriend {
//属性
private String name;
private int age;
private String gender;
//针对每一个私有化的成员变量,都要提供get和set方法
//set方法:给成员变量赋值
//get方法:对外提供成员变量的值
//作用:给成员变量name进行赋值
public void setName(String name) {
//等号左边是成员变量,表示成员位置的name
//等号右边是局部变量,表示测试类中调用方法传递过来的数据
this.name = name;
}
//作用:对外提供name属性
public String getName() {
return name;
}
//作用:对外提供age属性
public int getAge() {
return age;
}
//作用:给成员变量age进行赋值
public void setAge(int age) {
this.age = age;
}
//作用:对外提供gender属性
public String getGender() {
return gender;
}
//作用:给成员变量gender进行赋值
public void setGender(String gender) {
this.gender = gender;
}
}
测试类:
- 然后我们再去写测试类,测试类新建时如下:
- 需要先写主程序,然后创建新对象,再对对象赋值,最后调用相应的 get 方法就可以了,写出来的测试类为:
package com.fenghuai.oop.test3;
public class GirlFriendTest {
public static void main(String[] args) {
//创建新对象
GirlFriend GF = new GirlFriend();
GF.setName("人先欠着");
GF.setAge(18);
GF.setGender("女");
System.out.println("女朋友:" + GF.getName());
System.out.println("年龄是:" + GF.getAge());
System.out.println("性别是:" + GF.getGender());
}
}
- 运行结果如下:
- 在这个例子中大家可以看到对象类中的成员变量和局部变量虽然名称相同,但是可以通过this关键字区分开,后面我们可能会经常用到这种写法,大家可以多记忆一下这种写法。
八、实体 JavaBean(实体类)
1、标准的 JavaBean
①类名需要见名知意
②成员变量使用 private 修饰
③提供至少两个构造方法
- 无参构造方法
- 带全部参数的构造方法
④成员方法
- 提供每一个成员变量对应的 set() 和 get() 方法
- 如果还有其他行为,也需要写上
2、标准 JavaBean 举例
- 我们假设一个登录页面需要用户名、密码、邮箱、性别、年龄五个属性,然后我们下面写一个举例
package com.fenghuai.oop.test4;
public class User {
//属性
private String username;
private String password;
private String email;
private String genter;
private int age;
//空参构造
public User() {}
//带全部参数的构造
public User(String username, int age, String password, String email, String genter) {
this.username = username;
this.age = age;
this.password = password;
this.email = email;
this.genter = genter;
}
//get 和 set 方法
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getGenter() {
return genter;
}
public void setGenter(String genter) {
this.genter = genter;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3、快捷键:
- alt + insert会出现 generate 窗口,然后选择Constructor,这里可以快捷写出空参和全参构造。如果选择Getter and Setter可以快捷写出Getter and Setter构造。
九、面向对象编程综合案例
模范影视推荐系统
需求:
- 展示系统中的全部影视(每部影视展示:名称、影视类别、类型)。
- 允许用户根据影视编号(id)查询出某个电影的详细介绍。
分析:
- 需要写一个影视的实体类,需要写一个测试类
- 在实体类中写相应的构造方法(我们这里写标准的练练手,后面大家写的时候尽量也是写标准的,因为不知道什么时候就会用上)和成员方法
- 在测试类中写相应的内容,并与实体类连接起来
- 为测试类润色一下
解决:
- 实体类:编写相应属性和构造方法以及成员方法
package com.fenghuai.oop.test5;
public class Recommend {
//影视推荐的相关属性
private int id;//编号
private String name;//名称
private String category;//影视类别(电影/电视剧)
private String type;//类型
private String introduce;//详细介绍
//无参构造方法
public Recommend() {}
//有参构造方法
public Recommend(int id, String name, String category, String type, String introduce) {
this.id = id;
this.name = name;
this.category = category;
this.type = type;
this.introduce = introduce;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getIntroduce() {
return introduce;
}
public void setIntroduce(String introduce) {
this.introduce = introduce;
}
}
- 测试类:编写主程序,输入需要的数据,并与实体类连接起来
package com.fenghuai.oop.test5;
import java.util.Scanner;
public class RecommendTest {
public static void main(String[] args) {
int choice;
int id;
Recommend recommend1 = new Recommend();
Scanner scanner = new Scanner(System.in);
recommend1.setId(1);
recommend1.setName("沉默的真相");
recommend1.setCategory("电视剧");
recommend1.setType("悬疑、罪案");
recommend1.setIntroduce("该剧根据紫金陈的小说《长夜难明》改编,讲述检察官江阳受同学李静之托,帮助李静死去的男友侯贵平翻案,他与法医陈明章、警察朱伟历经十年光阴,付出青春、事业、名声、前途、家庭等无数代价追寻真相,侯贵平溺死案终在他们的执着寻找下水落石出的故事 ");
Recommend recommend2 = new Recommend();
recommend2.setId(2);
recommend2.setName("漫长的季节");
recommend2.setCategory("电视剧");
recommend2.setType("悬疑、罪案");
recommend2.setIntroduce("该剧讲述了出租车司机王响的儿子多年前跟一桩命案相关,死于非命,因为一起意外的套牌案,逃逸多年的凶手再次出现在桦林,王响和他的老伙计龚彪、辞职的老刑警马德胜组成民间探案三人组,踏上寻凶之旅。");
Recommend recommend3 = new Recommend();
recommend3.setId(3);
recommend3.setName("胆小鬼");
recommend3.setCategory("电视剧");
recommend3.setType("悬疑、罪案");
recommend3.setIntroduce("2001年冬,十八岁少女黄姝惨遭奸杀,全身赤裸被弃尸于一幢烂尾楼前的雪坑。大雪覆盖了胴体也掩盖了重要线索,作案手法和动机成迷。十年后,又一具少女尸体被剥光衣服丢进雪坑,作案手段完全复刻,可昔日重大嫌疑人却早已去世,追查此案的老刑警冯国金再次坠入迷雾。随着尘封旧案重见天日,一段深埋在四个少男少女间的残酷青春往事浮出水面。");
Recommend recommend4 = new Recommend();
recommend4.setId(4);
recommend4.setName("河边的错误");
recommend4.setCategory("电影");
recommend4.setType("悬疑、罪案");
recommend4.setIntroduce("《河边的错误》是余华先锋试验的系列作品之一,虚构了一个侦探故事。在神秘而充满诱惑的河边,么四婆婆被杀,刑警队长马哲负责侦查此案。证据表明,镇上的“疯子”是凶手,然而法律却对疯子无可奈何。接下来,疯子又接连杀人,忍无可忍的马哲亲手击毙了疯子,疯子死了,马哲却成了人们眼中的疯子。小说的主旨不在于戏仿,也不在于揭示暴力,而在于展示世界的死亡真相,以及探讨面对荒诞世界的生存哲学。");
System.out.println("-------------------------影视推荐系统-------------------------");
System.out.println("欢迎来到影视推荐系统!");
System.out.println("1、查询全部电影信息");
System.out.println("2、根据id查询影视详情");
System.out.println("请选择要操作的功能:");
choice = scanner.nextInt();
System.out.println("------------------------------------------------------------");
if (choice == 1) {
System.out.println(recommend1.getId() + " " + recommend1.getName() + " " + recommend1.getCategory() + " " + recommend1.getType());
System.out.println("------------------------------------------------------------");
System.out.println(recommend2.getId() + " " + recommend2.getName() + " " + recommend2.getCategory() + " " + recommend2.getType());
System.out.println("------------------------------------------------------------");
System.out.println(recommend3.getId() + " " + recommend3.getName() + " " + recommend3.getCategory() + " " + recommend3.getType());
System.out.println("------------------------------------------------------------");
System.out.println(recommend4.getId() + " " + recommend4.getName() + " " + recommend4.getCategory() + " " + recommend4.getType());
System.out.println("------------------------------------------------------------");
System.out.println("请选择影视id进行详细介绍:");
id = scanner.nextInt();
if (id == 1) {
System.out.println(recommend1.getIntroduce());
} else if (id == 2) {
System.out.println(recommend2.getIntroduce());
} else if (id == 3) {
System.out.println(recommend3.getIntroduce());
} else if (id == 4) {
System.out.println(recommend4.getIntroduce());
}
} else if (choice == 2) {
System.out.println("请选择影视id进行详细介绍:");
id = scanner.nextInt();
if (id == 1) {
System.out.println(recommend1.getIntroduce());
} else if (id == 2) {
System.out.println(recommend2.getIntroduce());
} else if (id == 3) {
System.out.println(recommend3.getIntroduce());
} else if (id == 4) {
System.out.println(recommend4.getIntroduce());
}
}
System.out.println("欢迎下次光临!");
System.out.println("-----------------------------------------------------------");
}
}
- 运行结果如下:
补充知识:成员变量、局部变量的区别小结
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置不同 | 类中、方法外 | 常见于方法中 |
初始化值不同 | 有默认值,不需要初始化赋值 | 没有默认值,使用之前必须完成赋值 |
内存位置不同 | 堆内存 | 栈内存 |
作用域不同 | 整个对象 | 在所归属的大括号中 |
生命周期不同 | 与对象共存亡 | 随着方法的调用而生,随着方法的运行结束而亡 |