面向对象编程
类和对象
什么是对象?
万物皆对象,客观存在的事物都是对象。
例如:一部手机,一条狗,一只猫,一张火车票,一个订单等等。
什么是类?
类是对具有相同特征和行为的事物的抽象。
例如:手机、狗、猫、火车票、订单、帅哥、美女等。
类其实就是类型。类的存在是为了弥补数据类型不足的问题。
什么是对象的属性?
属性是对象具有的各种特征。对象可以有很多个属性,每个属性都有特定的值。
例如:一部手机有品牌、价格、内存、颜色等属性,不同的手机属性值不同,下面是一部小米手机的属性。
什么是对象的行为?
行为就是对象能做什么事情,即对象能执行的操作。
例如:手机可以打电话,发短信等。
类和对象的关系
类是对象的抽象,用于描述对象,是对象的数据类型。
对象是类的具体体现,是类的实例。
例如: 手机是个类,用于描述什么是手机。手机应该具有品牌、价格、颜色等属性,具有打电话、发短信等功能。小米手机是个对象,华为手机是个对象。
再例如:人是一个类,张三是一个具体的人,是对象。电脑是个类,我的电脑是个对象。
注意:类可以细分,细分后的类也是类。细分的类可以认为是子类。
类是Java程序的基本组成单位,任何代码都要依托于类存在。想要做好Java开发必须要掌握类的定义以及对象的使用。
类的定义
类是对对象的抽象,是对对象的描述。它来定义对象都有哪些属性,有哪些行为。
因为对象包含属性和行为,由于类是对对象的描述,所以类也包含属性和行为。
对象的属性,在定义类的时候称为实例变量或成员变量或属性。
对象的行为,在定义类的时候称为实例方法或成员方法。
类本质上就是一种自定义的数据类型,定义出来类型以后,可以用这个类型去创建变量(创建的变量称为对象)。
类的定义格式
使用class关键字来定义类。
public class 类名{
//实例变量(或成员变量)
数据类型 变量1;
数据类型 变量2;
...
//实例方法(或成员方法)
方法1;//此方法要去掉static关键字
方法2;//此方法要去掉static关键字
...
}
注意事项:
1. 类名使用大驼峰结构命名,即每个单词首字母大写。
2. 实例方法不能用static修饰。
类的定义示例
1. 需求:定义上述的手机类。手机包含品牌、价格,能打电话、发短信。
2. 代码:
public class Phone {
//实例变量(成员变量)
String brand; //品牌
int price; //价格
//实例方法(成员方法)
public void call() {
System.out.println("打电话");
}
public void sendMessage() {
System.out.println("发短信");
}
}
一个文件中能定义多个类。----只要用class去定义就可以。
但只能有一个public修饰的类。被public修饰的类必须和文件名相同。
之所以只能有一个public修饰的类,是因为一个文件只有一个文件名。
大多数情况下一个文件中只定义一个类。
属性的默认值与数组元素的默认值相同:引用类型的默认值是null,基本数据类型默认值是: 整数是0,小数是0.0,布尔值是false,字符值是0。
对象的使用
对象的使用指的是:使用对象的属性 和 使用对象的方法。
要想使用对象,必须先创建对象。
对象的创建
对象创建的格式:
类名 对象名 = new 类名();
对象创建的示例:
Phone phone = new Phone();
如果在一个类中要使用别的类,分2种情况。
1. 别的类和当前类在同一个包中。不需要导包。
2. 别的类和当前类在不同的包中。需要导包。
对象的使用
使用对象的属性
使用对象的属性包括:给属性赋值、使用属性的值。
给对象的属性赋值格式:
对象名.属性名 = 值;
示例:
phone.brand = "iPhone";
phone.price = 6888;
使用对象的属性值格式:
数据类型 变量名 = 对象名.属性名;
示例:
String yourPhoneBrand = phone.brand;
int yourPhonePrice = phone.price;
使用对象的方法
使用对象的方法指的就是调用对象的方法。
格式:
对象名.方法名();//如果方法有参数,在小括号内写上实参。如果方法有返回值,可以用变量接收返回值。
示例:
phone.call();//调用打电话方法
phone.sendMessage();//调用发短信方法
测试类和对象
1. 新建一个类(ClassTest)用于测试Phone对象的使用,在main方法中创建1部手机,测试其属性和方法。
2. 代码:
public class ClassTest {
public static void main(String[] args) {
//创建phone对象
Phone phone = new Phone();
//给brand、price赋值
phone.brand = "iPhone";
phone.price = 6888;
//打印手机的brand和price
System.out.println(phone.brand);
System.out.println(phone.price);
//打电话
phone.call();
//发信息
phone.sendMessage();
}
}
对象在内存中的表示
单个对象在内存中的表示
public static void main(String[] args) {
//创建phone对象
Phone phone = new Phone();
System.out.println(phone);
//打印手机的brand和price
System.out.println(phone.brand);
System.out.println(phone.price);
//给brand、price赋值
phone.brand = "iPhone";
phone.price = 6888;
//打印手机的brand和price
System.out.println(phone.brand);
System.out.println(phone.price);
//打电话
phone.call();
//发信息
phone.sendMessage();
}
多个对象在内存中的表示
public static void main(String[] args) {
// 创建phone对象
Phone phone = new Phone();
System.out.println(phone);
// 打印手机的brand和price
System.out.println(phone.brand);
System.out.println(phone.price);
// 给brand、price赋值
phone.brand = "iPhone";
phone.price = 6888;
// 打印手机的brand和price
System.out.println(phone.brand);
System.out.println(phone.price);
// 打电话
phone.call();
// 发信息
phone.sendMessage();
System.out.println("--------------");
// 创建phone对象
Phone phone2 = new Phone();
System.out.println(phone2);
// 打印手机的brand和price
System.out.println(phone2.brand);
System.out.println(phone2.price);
// 给brand、price赋值
phone2.brand = "华为";
phone2.price = 2999;
// 打印手机的brand和price
System.out.println(phone2.brand);
System.out.println(phone2.price);
// 打电话
phone2.call();
// 发信息
phone2.sendMessage();
}
多个对象指向相同堆区
public static void main(String[] args) {
// 创建phone对象
Phone phone = new Phone();
System.out.println(phone);
// 打印手机的brand和price
System.out.println(phone.brand);
System.out.println(phone.price);
// 给brand、price赋值
phone.brand = "iPhone";
phone.price = 6888;
// 打印手机的brand和price
System.out.println(phone.brand);
System.out.println(phone.price);
// 打电话
phone.call();
// 发信息
phone.sendMessage();
System.out.println("--------------");
Phone phone2 = phone;
System.out.println(phone2);
// 打印手机的brand和price
System.out.println(phone2.brand);
System.out.println(phone2.price);
// 给brand、price赋值
phone2.brand = "华为";
phone2.price = 2999;
// 打印手机的brand和price
System.out.println(phone2.brand);
System.out.println(phone2.price);
// 打电话
phone2.call();
// 发信息
phone2.sendMessage();
}
new出来的内存区域才是真正的对象。
phone和phone2只是保存了对象的地址,相当于phone和phone2代表了对象。我们习惯把这种保存了对象地址的变量叫做对象。
如果多个变量指向同一个对象,那么任何一个变量都可以修改对象的内容。
类和对象练习
学生类
1. 需求:创建一个代表学生的类,并创建学生对象测试效果。属性只写出姓名、年龄即可,方法只写出学习、做作业即可。
2. 代码:
学生类代码:
public class Student {
//属性
String name;//姓名
int age;//年龄
//方法
public void study() {
System.out.println(name + "正字努力学习Java面向对象");
}
public void doHomework() {
System.out.println("十万代码成就十万年薪。" + name + "正在努力做练习,离10万年薪越来越近了");
}
}
测试代码:
public static void main(String[] args) {
Student stu = new Student();
stu.name = "马化腾";
stu.age = 22;
stu.study();
stu.doHomework();
Student stu2 = new Student();
stu2.name = "李彦宏";
stu2.age = 19;
stu2.study();
stu2.doHomework();
}
实例变量(成员变量)和局部变量:
1. 实例变量:定义在类中方法外的变量。
2. 局部变量:在方法中定义的变量(包括方法的形参)。
圆
1. 设计一个代表圆的类。要求能计算圆的周长和面积。
2. 代码:
圆类代码:
public class Circle {
//属性
double r; //半径
//方法
//计算周长
public double primeter() {
return 2 * 3.1415926 * r;
}
//计算面积
public double area() {
return 3.1415926 * r * r;
}
}
测试代码:
public static void main(String[] args) {
Circle c1 = new Circle();
c1.r = 10;
System.out.println(c1.primeter());
System.out.println(c1.area());
Circle c2 = new Circle();
c2.r = 4;
System.out.println(c2.primeter());
System.out.println(c2.area());
}
教师数组
1. 定义一个教师类,教师包含工号(num)和工资(salary)两个属性。定义一个数组,保存10名教师的信息。教师的编号从100开始,教师的工资是[8k, 15k]之间的一个数。打印出薪资是12k的教师的信息。
2. 代码:
教师类代码:
public class Teacher {
int num; //工号
int salary; //工资
public void showInfo() {
System.out.println("工号:" + num + ",工资:" + salary);
}
}
测试代码:
public static void main(String[] args) {
// 创建一个数组,保存10个教师
Teacher[] teachers = new Teacher[10];
// 创建Random对象,用于随机工资。
Random random = new Random();
// 通过循环创建10个教师,并放入数组中。
for (int i = 0; i < teachers.length; i++) {
Teacher t = new Teacher();
t.num = i + 100;
t.salary = (random.nextInt(15 - 8 + 1) + 8) * 1000;
teachers[i] = t;
}
// 遍历数组
for (int i = 0; i < teachers.length; i++) {
teachers[i].showInfo();
}
System.out.println("-----------");
// 查找工资为12k的教师
for (int i = 0; i < teachers.length; i++) {
if (teachers[i].salary == 12000) {
teachers[i].showInfo();
}
}
}
面向对象编程、面向过程编程
面向对象编程(Object Oriented Programing)是相对于面向过程编程(Procedure Oriented Programing)而言的。它们是2种不同的编程思想。
面向过程编程(POP)
面向过程编程是一种以功能为中心来进行思考和组织的编程方法,它关注的是功能如何实现,就是分析出解决问题所需要的的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个调用就可以了。----关注的是功能的实现
面向对象编程(OOP)
面向对象编程是一种以事物(对象)为中心的编程思想,它关注的是完成某件事情需要哪些对象参与,这些对象应该具有什么样的属性和方法。通过对象的协作完成某件事情。----关注的是参与者(类)怎么设计
面向对象编程和面向过程编程的区别
需求:把大象关进冰箱里
面向过程编程
1. 编写函数实现打开冰箱门的功能
编写函数实现把大象装进去的功能
编写函数实现把冰箱门关闭的功能
2. 在main函数中依次调用函数:打开冰箱门、把大象装进去、把冰箱门关上。
面向对象编程
1. 分析把大象关进冰箱需要什么对象参与:大象、冰箱、人。
2. 设计各个类
public class 冰箱{
public void 开门(){
...
}
public void 关门(){
...
}
}
public class 大象{
public void 进入(冰箱 icebox){
...
}
}
public class 人{
public void 打开(冰箱 icebox){
icebox.开门();
}
public void 抬起(大象 elephant){
...
elephant.进入(icebox);
...
}
public void 关闭(冰箱 icebox){
icebox.关门();
}
}
3. 在main方法中:
创建人(张三),创建冰箱(海尔冰箱),创建大象(艾米丽)
张三.打开(海尔冰箱);
张三.抬起(艾米丽);
张三.关闭(海尔冰箱);
无论面向过程,还是面向对象都能实现把大象装进冰箱这么一件事。从上述的过程中,我们不难发现,面向过程编程的核心是完成某件事;而面向对象的核心是设计参与者,最后才去完成具体的事。
项目种类越大,面向对象越有优势,而面向过程这越难以实现。例如:拍一个几分钟的小品,可以使用面向过程的思想,从头到尾设计每个细节。但拍一部电影,或者拍摄一部电视剧就得用面向对象的思想了,对每个参与者进行分工,各自干好自己的事情,镜头也未必从前往后拍摄,今天下雨可以先拍下雨的戏份,今天到场的演员多,可以拍摄演员多的戏份,最后进行剪辑拼接制作成电影和电视剧。
封装
面向对象语言有三大特性:封装、继承和多态。
在讲解封装(encapsulation)的概念之前,我们先学习一下private关键字。
private
先看下面的代码:
有一个Student类
public class Student {
//实例变量
String name;//姓名
int age;//年龄
//实例方法
public void study() {
System.out.println(name + "正在努力学习Java面向对象的知识。");
}
public void doHomework() {
System.out.println(name + "在努力的写作业。十万行代码成就你十万年薪");
}
public void showInfo() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
Student类创建了2个对象。
public static void main(String[] args) {
Student stu1 = new Student();
stu1.name = "马化腾";
stu1.age = 22;
stu1.showInfo();
Student stu2 = new Student();
stu2.name = "李彦宏";
stu2.age = -20;
stu2.showInfo();
}
输出结果:
上述的输出结果是没有错的,程序也正常执行的。但是人的年龄是-20是有点违背逻辑的。
之所以出现这种问题,是因为外界可以直接操作属性。想要避免这种问题,最好的办法是不让外界直接操作属性,把属性隐藏起来,给外界提供访问属性的方法。
通过private把属性隐藏起来
private单词的含义是私有的。private可以修饰类中的属性和方法,修饰属性的时候,表示属性私有,外界无法直接访问(所谓的直接访问是通过对象名.属性名访问),但是在本类中可以访问。
public class Student {
//实例变量
private String name;//姓名
private int age;//年龄
//实例方法
public void study() {
System.out.println(name + "正在努力学习Java面向对象的知识。");
}
public void doHomework() {
System.out.println(name + "在努力的写作业。十万行代码成就你十万年薪");
}
public void showInfo() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
上述代码实现了属性的私有,一旦私有,外界将不能使用对象名.属性名去访问对象的属性。
通过setter、getter方法访问属性
setter方法:为属性(实例变量)赋值的方法。
getter方法:获取属性(实例变量)值的方法。
由于getter、setter方法是我们特意为外界定义的方法,方便外界能访问属性,因此要有public修饰。
public String getName() {
return name;
}
public void setName(String n) {
name = n;
}
public int getAge() {
return age;
}
public void setAge(int a) {
age = a;
}
setter方法既然是为属性赋值,就必须要有参数,而且参数的类型和属性的类型相同。setter方法的方法名是set+首字母大写的属性名。例如:属性名是name,setter方法的方法名是setName,属性名是age,setter方法的方法名是setAge。
getter方法既然是要获取属性的值,就必须有返回值,而且返回值的类型和属性的类型相同。getter方法的方法名是get+首字母大写的属性名。例如:属性名是name,getter方法的方法名是getName,属性名是age,getter方法的方法名是getAge。
提供了方法以后,外界就可以通过方法去访问属性。
public static void main(String[] args) {
Student stu1 = new Student();
stu1.setName("马化腾");
stu1.setAge(22);
stu1.showInfo();
Student stu2 = new Student();
stu2.setName("李彦宏");
stu2.setAge(-20);
stu2.showInfo();
}
输出结果:
输出结果还是-20,并没有解决任何问题呀!稍作改进,改进代码如下:
public void setAge(int a) {
if(a < 0) {
System.out.println("您输入的年龄有误。");
}else {
age = a;
}
}
输出结果:
setter/getter和直接访问的异同
相同点:都是在访问属性,包括赋值和取值。
不同点:1.直接访问属性优势是简单快捷,一步到位;劣势是有可能会出现数据错误。2.通过setter/getter访问属性优势是数据没有直接赋值给属性,在赋值之前可以做一些操作和处理;劣势是代码量略大。
在开发中属性一般情况下都定义private,对外提供pulic的getter和setter方法。
this
讲解this之前,先看一下刚才写的setter、getter方法
public String getName() {
return name;
}
public void setName(String n) {
name = n;
}
public int getAge() {
return age;
}
public void setAge(int a) {
if(a < 0) {
System.out.println("您输入的年龄有误。");
}else {
age = a;
}
}
上面方法中的参数并没有做到见名知意。如果做到见名知意,应改为:
public String getName() {
return name;
}
public void setName(String name) {
name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age < 0) {
System.out.println("您输入的年龄有误。");
}else {
age = age;
}
}
改成这样以后,确实见名知意了,但是程序的结果却不对了。
这是因为:在一个类中,如果局部变量和实例变量名称相同,在方法中使用变量的时候,使用的是局部变量,而不是实例变量。想要在方法中使用实例变量的话,需要使用this.实例变量名。
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age < 0) {
System.out.println("您输入的年龄有误。");
}else {
this.age = age;
}
}
this的英文含义是这个,在代码中this是一个特殊的对象,它始终指的是调用方法的对象,即谁调用方法,this就是谁。
可以通过打印内存地址的方式,来检验this究竟是谁。
this关键字
1. this修饰的变量用于指代实例变量。方法的形参如果与实例变量同名,不带this的变量是形参,而不是实例变量。方法的形参如果和实例变量不同名,不需要用this修饰。
2. this代表的就是调用方法的那个对象。
构造方法
构造方法是一种特殊的方法,它是创建对象时调用的方法,用于在创建对象的时候对属性进行初始化。
Student stu1 = new Student();//Student()就是一个构造方法。
构造方法的特点
1. 构造方法只能用在对象创建的时候。
2. 构造方法的作用是对属性进行初始化。
3. 构造方法的方法名必须和类名相同。
4. 构造方法没有返回值,连void都不能写。
5. 如果自己没有提供构造方法,系统会默认生成一个无参数的构造方法。
6. 如果自己提供了任何一个构造方法,系统将不再生成无参数的构造方法。
7. 构造方法可以重载。
构造方法的书写格式
public 类名(参数列表){
}
构造方法示例
public Student() {
}
public Student(String name) {
this.name = name;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
上面的三个方法都是构造方法。
构造方法的使用
public static void main(String[] args) {
Student stu1 = new Student("马化腾", 22);
stu1.showInfo();
Student stu2 = new Student("李彦宏");
stu2.setAge(20);
stu2.showInfo();
Student stu3 = new Student();
stu3.setName("马云");
stu3.setAge(25);
stu3.showInfo();
}
在开发过程中,我们通过会至少给2个构造方法。其中一个是无参构造方法,另外一个是全参的构造方法。除此以外根据需求添加别的构造方法。
标准的Java类
标准Java类的定义原则
1. 属性用private修饰
2. 提供属性对应的setter、getter方法
3. 提供1个或多个构造方法
4. 提供正常的功能性方法
标准Java类示例
public class Circle {
//属性
private double r; //半径
public double getR() {
return r;
}
public void setR(double r) {
this.r = r;
}
public Circle() {
}
public Circle(double r) {
this.r = r;
}
//实例方法
public double perimeter() {
return 2 * Math.PI * r;
}
public double area() {
return Math.PI * r * r;
}
}
使用圆的时候:
public static void main(String[] args) {
// 使用无参构造创建对象
Circle c1 = new Circle();
c1.setR(10);
c1.perimeter();
c1.area();
// 使用有参构造创建对象
Circle c2 = new Circle(4);
c2.perimeter();
c2.area();
}
封装
封装:隐藏对象的内部细节,对外提供接口(访问方式)。
封装的原则:将类的某些信息隐藏在类内部,不允许外界直接访问,而是给外界提供接口(方法)。外界通过接口访问内部的数据以及类的功能。
1. getter、setter封装了实例变量。
2. 方法封装了功能的实现细节。
3. 类封装了属性和方法。
封装的好处:
1. 通过方法来控制实例变量的操作,提高了代码的安全性。
2. 把代码用方法进行封装,提高了代码的复用性。
常用
定义类使用class关键字。
类中的属性通常用private修饰,对外提供setter/getter方法。
类中的方法通常用public修饰。
类中应根据需要提供适当数量的构造方法。
对象需要先创建再使用,创建对象的语法是: 类名 对象名 = new 类名();// 类名()指的是类的构造方法。