06_面向对象
一、类和对象
面向对象和面向过程的思想对比:
- 面向过程:是一种以过程为中心的编程思想,实现功能的每一步,都是自己实现的
- 面向对象:是一种以对象为中心的编程思想,通过指挥对象实现具体的功能
1.1 类和对象的关系
客观存在的事物皆为对象,所以我们也常常说万物皆对象
- 类
- 类的理解
- 类是对现实生活中一类具有共同属性和行为的事物的抽象
- 类是对象的数据类型,类是具有相同属性和行为的一组对象的集合
- 简单理解:类就是对现实事物的一种描述
- 类的组成
- 属性:指事物的特征,例如:手机事物(品牌、价格、尺寸)
- 行为:指事物能执行的操作,例如:手机事物(打电话、发短信)
- 类的理解
- 类和对象的关系
- 类:类是对现实生活中一类具有共同属性和行为的事物的抽象
- 对象:是能够看得到摸得着的真实存在的实体
- 简单理解:类是对事物的一种描述,对象则是具体存在的事物
1.2 类的定义(应用)
类的组成是由属性和行为2部分组成
- 属性:在类中通过成员变量来体现(类中方法外的变量)
- 行为:在类中通过成员方法来体现
类的定义步骤:
- 定义类
- 编写类的成员变量
- 编写类的成员方法
package com.it.pojo;
// 学生类
public class Student {
// 属性:姓名、年龄
// 成员变量:类中方法外
String name;
int age;
// 行为:学习
// 成员方法
public void study(){
System.out.println("学习");
}
}
1.3 对象的创建和使用
-
创建对象的格式:
类名 对象名 = new 类名();
-
调用成员的格式:
- 对象名.成员变量
- 对象名.成员方法();
-
实例代码
package com.it.test; public class TestStudent { public static void main(String[] args) { // 类名 对象名 = new 类名(); Student stu = new Student(); // 对象名.变量名 // 默认初始化值 System.out.println(stu.name); // null System.out.println(stu.age); // 0 // 设置值 stu.name = "张三"; stu.age = 29; System.out.println(stu.name); // 张三 System.out.println(stu.age); // 29 // 修改值 stu.name = "李四"; System.out.println(stu.name); // 李四 // 对象名.方法名(); stu.study(); // 学习 System.out.println(stu); // com.it.test.Student@1540e19d 全类名(包名 + 类名) } }
1.4 案例
-
需求
手机类的创建和使用,首先定义一个手机类,然后定义一个手机测试类,在手机测试类中通过对象完成成员变量和成员方法的使用
-
分析:
1.成员变量:品牌、价格
2.成员方法:打电话、发短信
-
示例代码:
Phone.java
package com.it.test; public class Phone { // 品牌、价格 String brand; int price; // 打电话 public void call(String name){ System.out.println("给" + name + "打电话"); } // 发短信 public void sendMessage(){ System.out.println("群发短信"); } }
TestPhone.java
package com.it.test; public class TestPhone { public static void main(String[] args) { // 创建对象 Phone p = new Phone(); // 给成员变量赋值 p.brand = "荣耀"; p.price = 2999; // 打印赋值后的成员变量 System.out.println(p.brand + "..." + p.price); // 荣耀...2999 // 调用成员方法 p.call("雷军"); // 给雷军打电话 p.sendMessage(); // 群发短信 } }
二、对象内存图
2.1 示例
Student.java
package com.it.test;
public class Student {
// 属性:姓名、年龄
// 成员变量:类中方法外
String name;
int age;
// 行为:学习
// 成员方法
public void study(){
System.out.println("学习");
}
}
TestStudent.java
package com.it.test;
public class TestStudent {
public static void main(String[] args) {
// 类名 对象名 = new 类名();
Student stu = new Student();
// 对象名.变量名
// 默认初始化值
System.out.println(stu.name); // null
System.out.println(stu.age); // 0
// 设置值
stu.name = "张三";
stu.age = 29;
System.out.println(stu.name); // 张三
System.out.println(stu.age); // 29
// 修改值
stu.name = "李四";
System.out.println(stu.name); // 李四
// 对象名.方法名();
stu.study(); // 学习
System.out.println(stu); // com.it.test.Student@1540e19d 全类名(包名 + 类名)
}
}
流程:
- 测试类的字节码文件(TestStudent.class)加载到方法区,这个里面有一个主方法字节码(main)会被加载
- 主方法会进入栈内存去执行
- 执行到Student s的时候,Student.class会在方法区加载,会将study()方法加载进方法区
- new Student();的时候,会在堆内存中开辟一块空间,会将成员变量name、age加载在这里 String name = null,int age = 0;同时,也会在堆内存中的这块地址里,存储study()成员方法的地址,指向方法区中的study()方法所在的字节码
- 创建完毕,这时,堆内存中的这块区域也有一个地址,比如001stu,会赋值给 Student s = 001stu;,现在通过s这个变量,就可以找到堆内存中的这块空间
- System.out.println(s); 打印这个地址值
- System.out.println(s.name); 打印这个属性
- s.name = "张三",将堆内存中的这块地址的name属性改为张三
- 执行到s.study()的时候,通过s找到堆内存中的这块地址,.study(),通过这个方法的引用去找方法区的这个地址,然后把方法加载到栈内存中去执行
- study()方法执行完毕,就弹栈
- 主方法(main)执行完毕,也弹栈
2.2 单个对象内存图
2.3 多个对象内存图
总结:
多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自的内存区域中,成员方法多个对象共用一份
2.4 多个对象指向相同内存图
总结:
当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的),只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据
三、成员变量和局部变量
3.1 成员变量和局部变量的区别
- 类中位置不同:成员变量是在类中方法外,局部变量是在方法内部或方法声明上
- 内存中位置不同:成员变量在堆内存中,局部变量在栈内存中
- 生命周期不同:成员变量随着对象的存在而存在,随着对象的消失而消失,局部变量随着方法的调用而存在,随着方法的调用完毕而消失
- 初始化值不同:成员变量有初始化值,局部变量没有默认初始化值,必须先定义,赋值后才能使用
四、封装
4.1 private关键字
-
概述
private
是一个修饰符,可以用来修饰成员(成员变量、成员方法) -
特点
被
private
修饰的成员,只能在本类进行访问,针对private
修饰的成员变量,如果需要被其他类使用,提供相应的操作- 提供
get
变量名() 方法 ,用于获取成员变量的值,方法用public
修饰 - 提供
set
变量名(参数) 方法,用于设置成员变量的值,方法用public
修饰
- 提供
-
示例代码
Student.java
package com.it.test; // 学生类 public class Student { // 成员变量 String name; private int age; // 提供get、set方法 public int getAge() { return age; } public void setAge(int a) { if(a < 0 || a > 120){ System.out.println("你给的年龄有误"); }else{ age = a; } } // 成员方法 public void show(){ System.out.println(name + "," + age); } }
StudentDemo.java
package com.it.test; public class StudentDemo { public static void main(String[] args) { // 创建对象 Student stu = new Student(); // 给成员变量赋值 stu.name = "林青霞"; stu.setAge(30); // 调用成员方法 stu.show(); // 林青霞,30 } }
4.2 private关键字的使用
-
需求:
- 定义标准的学生类,要求
name
和age
使用private
修饰 - 并提供
get
和set
方法及便于显示数据的show
方法 - 测试类中创建对象并使用,最终控制台输出 林青霞,30
- 定义标准的学生类,要求
-
示例代码
Studnet.java
package com.it.test; // 学生类 public class Studnet { // 成员变量 private String name; private int age; // get、set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // 成员方法 public void show(){ System.out.println(name + "," + age); } }
StudentDemo.java
package com.it.test; // 学生测试类 public class StudentDemo { public static void main(String[] args) { // 创建对象 Studnet stu = new Studnet(); // 使用set方法给成员变量赋值 stu.setName("林青霞"); stu.setAge(30); // 调用成员方法 stu.show(); // 林青霞,30 // 使用get方法获取成员变量的值 System.out.println(stu.getName() + "---" + stu.getAge()); // 林青霞---30 } }
4.3 this关键字
-
概述
this
修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)- 方法的形参如果与成员变量同名,不带
this
修饰的变量指的是形参,而不是成员变量 - 方法的形参没有与成员变量同名,不带
this
修饰的变量指的是成员变量
-
代码实现
package com.it.test; // 学生类 public class Studnet { // 成员变量 private String name; private int age; // get、set方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } // 成员方法 public void show(){ System.out.println(name + "," + age); } }
4.4 this内存原理
-
概述
this
代表当前调用方法的引用,哪个对象调用的方法,this
就代表哪一个对象 -
图解
4.5 封装思想
-
概述
是面向对象的三大特征之一(封装、继承、多态)
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的
-
原则
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
成员变量
private
,提供对应的get
、set
方法 -
好处
通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性
五、构造方法
5.1 构造方法的格式和执行时机
-
格式
- 方法名与类名相同,大小写也要一致
- 没有返回值类型,连
void
都没有 - 没有具体的返回值(不能由
return
带回结果数据)
-
执行时机
- 创建对象的时候调用,每创建一次对象,就会执行一次构造方法
- 不能手动调用构造方法
-
示例
Student.java
package com.it.test02; public class Student { private String name; private int age; public Student() { System.out.println("无参构造方法"); } public void show(){ System.out.println(name + "," + age); } }
StudentDemo.java
package com.it.test02; public class StudentDemo { public static void main(String[] args) { Student stu = new Student(); // 无参构造方法 stu.show(); // null,0 } }
5.2 构造方法的作用
用于给对象的数据(属性)进行初始化
Student.java
package com.it.test03;
public class Student {
private String name;
private int age;
// 如果一个类中没有编写任何构造方法,系统将会提供一个默认的无参数构造方法
public Student() {
}
// 如果手动编写了构造方法,系统就不会再提供默认的无参数构造方法了
public Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("我是Student类的构造方法");
}
public void show(){
System.out.println(name + "," + age);
}
}
StudentDemo.java
package com.it.test03;
public class StudentDemo {
public static void main(String[] args) {
Student stu = new Student("张三", 20); // 我是Student类的构造方法
stu.show(); // 张三,20
}
}
5.3 构造方法的注意事项
-
构造方法的创建
- 如果没有定义构造方法,系统将会给出一个默认的无参数构造方法
- 如果定义了构造方法,系统将不再提供默认的构造方法
-
推荐的使用方式
无论是否使用,都手动书写无参数构造方法和带参数构造方法
5.4 标准类的代码编写和使用
Student.java
package com.it.test04;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println(name + "," + age);
}
}
StudentDemo.java
package com.it.test04;
public class StudentDemo {
public static void main(String[] args) {
// 无参数构造方法创建对象,通过set方法给成员变量进行赋值
Student stu = new Student();
stu.setName("张三");
stu.setAge(20);
stu.show(); // 张三,20
// 通过带参数的构造方法,直接给属性进行赋值
Student stu2 = new Student("李四", 23);
stu2.show(); // 李四,23
}
}
问题
- 为啥不同的包不能对象.属性名获取属性,同一个包可以?