文章目录
- 面向对象
- 类和对象基本使用
- 对象的内存图
- 构造器
- this关键字
- 对象三大特征: 封装
- 成员变量和局部变量区别
面向对象
面向对象介绍
- 并不是一个技术,而是一种编程指导思想。
- 把现实世界的具体事物全部看成一个一个的对象来解决实际问题。
为什么要用面向对象编程?
- 生活中我们解决问题就是按照对象化的方式进行的。如果程序也能够按照生活的中的方式来解决问题,那么程序就更符合人类的思维习惯,代码看起来会更易理解、更简单、更易维护
面向对象重点学习什么?
- 学习如何自己设计对象并使用
- 学习获取已有对象并使用
类和对象基本使用
类(设计图):是对象共同特征的描述, 相当于设计图纸
对象:是真实存在的具体实例。
在Java中,必须先设计类,才能获得对象。
我们看一下如何定义类, 并通过类获取对象的?
- 定义类的方法如下
public class 类名 {
// 1、成员变量(代表属性)
// 2、成员方法(代表行为)
// 3、构造器 (后面学习)
// 4、代码块 (后面学习)
// 5、内部类 (后面学习)
}
public class Car {
// 属性 (成员变量)
String name;
double price;
// 方法 (成员行为)
public void start(){
}
public void run(){
}
}
- 当有了类之后, 我们就可以获取对象:
类名 对象名 = new 类名();
Car c1 = new Car();
- 获取到对象之后, 我们就可以使用对象的属性和方法
- 访问属性:
对象名.成员变量
- 访问方法:
对象名.成员方法
// 访问属性
c1.name;
c1.parice;
// 访问方法
c1.start();
c1.run();
定义类的补充注意事项
- 成员变量的完整定义格式是:
修饰符 数据类型 变量名称 = 初始化值
; 但是不同对象的变量值肯不同, 所以一般无需指定初始化值,存在默认值。 - 类名首字母建议大写,且有意义,满足“大驼峰模式”。
- 一个Java文件中可以定义多个class类,且只能一个类是public修饰,而且public修饰的类名必须成为代码文件名。
- 实际开发中建议还是一个文件定义一个class类。
对象的内存图
例如我们有如下两个类, 他们在内存中是如何执行的呢?
public class Car {
// 成员变量(属性)
String name;
double price;
// 方法(行为)
public void start(){
System.out.println(name+"启动了!");
}
public void run(){
System.out.println("售价为:" + price +"的" + name+"跑的快!");
}
}
public class Test {
public static void main(String[] args) {
Car c1 = new Car();
c1.name = "奔驰GLC";
c1.price = 39.78;
System.out.println(c1.name);
System.out.println(c1.price);
c1.start();
c1.run();
Car c2 = new Car();
c2.name = "宝马X3";
c2.price = 38.98;
System.out.println(c2.name);
System.out.println(c2.price);
c2.start();
c2.run();
}
}
- 我来画图, 分步骤给大家讲解一下在内存中是如何执行的
- 首先会执行Test类, 将Test类中的main方法提到方法区
- 执行main方法, 将main方法放入栈内存中执行
- 依次执行main方法中的语句
- 执行第一行语句
Car c1 = new Car();
时, 会将Car这个类(以及他的属性和方法)提到方法区 - 在栈内存中声明一个变量, 由于是new出来的对象, 会放在堆内存, 那么声明的变量c1保存的是堆内存中的地址
new Car
在对象中开辟出来的空间, 会存放Car这个类的属性和方法, 由于属性未初始化, 是默认值, 方法存放的是引用地址
- 继续执行main方法的代码
c1.name = "奔驰GLC";
将堆内存中的name属性赋值为: 奔驰GLCc1.price = 39.78;
将堆内存中的price属性赋值为: 39.78System.out.println(c1.name); System.out.println(c1.price);
再打印name和price
- 后面执行方法的代码
- 会根据c1保存的地址找到start方法和run方法
- 再分别将start方法和run方法分别放入栈内存中执行
- 下面main方法中的c2同理, 会在堆内存中重新开辟一个新的空间, 将地址赋值给栈内存中的变量c2
垃圾回收 :
- 注意:当堆内存中的类对象或数组对象,没有被任何变量引用(指向)时,就会被判定为内存中的“垃圾”。
- Java存在自动垃圾回收器,会定期进行清理。
构造器
学习目的:
- 真正知道对象具体是通过什么得到的。
- 能够掌握为对象赋值的其他写法。
构造器的作用 :
- 用于初始化一个类的对象,并返回对象的地址。
构造器的定义格式 : 修饰符 类名(形参列表){}
构造器的分类 :
- 无参数构造器(默认存在的):初始化的对象时,成员变量的数据均采用默认值。
- 有参数构造器:在初始化对象的时候,同时可以为对象进行赋值。
- 示例如下 :
public class Car {
// 无参数构造器
public Car(){
...
}
// 有参数构造器
public Car(String n, String b){
...
}
}
前面我们所使用的其实就是无参数构造器, 因为无参数构造器是默认存在的
我们来看一下有参数构造器如何使用吧 :
- 例如: 定义了如下一个类, 类中使用有参数构造器
public class Test {
String name;
int age;
double height;
// 有参数构造器
public Test(String a, int b, double c) {
name = a;
age = b;
height = c;
}
}
- 那么我们创建对象的时候, 就可以传入数据
- 不需要像无参数构造器一样, 拿到实例对象后, 还需要依次赋值
public static void main(String[] args) {
Test test1 = new Test("chenyq", 18, 1.88);
System.out.println(test1.name); // chenyq
System.out.println(test1.age); // 18
System.out.println(test1.height); // 1.88
}
注意事项 :
- 任何类定义出来,默认就自带了无参数构造器,写不写都有。
- 一旦定义了有参数构造器,无参数构造器就没有了,此时就需要自己写一个无参数构造器了。
this关键字
this关键字可以出现在成员方法、构造器中,代表当前对象的地址。
- 作用:访问当前对象的成员变量、成员方法。
this出现在有参数构造器中的用法 :
- 例如: 刚刚我们在类中定义的有参数构造器可以写成下面这种格式
public class Test {
String name;
int age;
double height;
public Test(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
}
this出现在成员方法中的用法 :
public class Test {
String name = "chenyq";
public void introduce() {
System.out.println("my name is" + " " + this.name);
}
public Test(String name) {
this.name = name;
}
}
对象三大特征: 封装
面向对象的三大特征:封装,继承,多态。
- 什么是封装? 隐藏实现细节,暴露出合适的访问方式。(合理隐藏、合理暴露)
为什么要用封装?我们来看下面的例子 :
- 定义如下一个类:
public class Student {
int age;
}
- 设置年龄变量
Student s = new Student();
s.age = -23;
我们发现, 上面的做法是允许的, 但是从业务角度来说, 其实是不规范的(因为年龄没有负值), 外部可以随意修改我们内部的变量是比较危险的
封装的实现步骤 :
- 一般对成员变量使用private关键字修饰进行隐藏,private修饰后该成员变量就只能在当前类中访问。
- 提供public修饰的公开的getter、setter方法暴露其取值和赋值。
示例代码 :
- 对类中的操作
public class Student {
// private定义的变量, 外部无法访问
private int age;
// 1.通过getter函数, 暴露出去
public int getAge() {
return this.age;
}
// 2.通过setter函数, 暴露出去同时又可以设置
public void setAge(int age) {
// 判断age符合规范再设置
if (age >= 0 && age <= 200) {
this.age = age;
} else {
System.out.println("请输入正确的年龄");
}
}
}
- 设置获取的演示
public static void main(String[] args) {
Student stu1 = new Student();
// 通过setter函数设置age
stu1.setAge(18);
// 通过getter函数获取age
System.out.println(stu1.getAge());
}
封装的好处小结 :
- 加强了程序代码的安全性
- 适当的封装可以提升开发效率,同时可以让程序更容易理解与维护。
标准的封装需满足以下要求 :
- 成员变量使用 private
- 提供每一个成员变量对应的 setXxx() / getXxx()。
- 必须提供一个无参构造器
成员变量和局部变量区别
区别 | 成员变量 | 局部变量 |
类中位置不同 | 类中,方法外 | 常见于方法中 |
初始化值不同 | 有默认初始化值 | 没有,使用之前需要完成赋值 |
内存位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随着对象的创建而存在,随着对象的消失而消失 | 随着方法调用而存在, 随着方法运行结束消失 |
作用域 | 在所归属的大括号中 |