云计算百科
云计算领域专业知识百科平台

Java 面向对象基础(类与对象)—— Java 的灵魂与骨架

面向对象

Java 面向对象基础是 Java 语言的核心思想与设计基石,面向对象是 Java 区别于面向过程语言的本质特征,贯穿 Java 语言设计的始终

OOP(object oriented programming),面向对象编程
是一种以对象为中心的编程思想,通过借助对象实现具体的功能
将大问题拆分成小问题,然后借助不同对象分别解决,最终实现功能

POP(procedure oriented Programming),面向过程编程
是一种以过程为中心的编程思想,靠自己一步一步去实现功能,需要对每个步骤精确控制
强调按步骤实现功能,先分析解决问题所需步骤,再自定义方法实现每个步骤功能,然后依次调用方法,最终实现功能


面向对象这个抽象的概念通常与面向过程同时出现,而且只用  注重过程~ 和对应的  注重结果?😳 这个解释很难让人真正理解,接下来我将举一些例子帮助大家理解面向对象这个概念

简单来说:

面向过程(Procedure-Oriented)更关注 “怎么做”(步骤和流程),通过函数串联起一系列操作,一步一步实现目标;
面向对象(Object-Oriented)更关注 “谁来做”(实体和交互),通过抽象出对象,让对象之间协作完成目标,过程被封装在对象的行为中。

举例来说,我们要网购的话,用面向过程来说,就是会拆解出具体步骤,按顺序执行:

步骤1:打开手机,打开购物APP
步骤2:搜索“拖鞋”,选一个想要的
步骤3:确认数量,提交订单
步骤4:在线支付
步骤5:等待配送,收到

如果是面向对象,就是会抽象出参与的 “对象”,以及对象的属性和行为,通过对象协作完成:

对象 1:用户(属性:账户余额;行为:打开 APP、选择商品、支付)
对象 2:购物 APP(属性:商品列表;行为:搜索商品、生成订单)
对象 3:支付系统(属性:支付渠道;行为:处理支付)
对象 4:配送员(属性:位置;行为:配送商品)

不是说Java更符合咱们的思考习惯吗,我用我个人比较喜欢的一个例子来说就是,我们关门的话,关门这个动作过程就是面向过程要考虑的,而面向对象就是门可以被关上(或者打开),我们使用了它这个能力,这个例子也变相说明了为什么不把关门这个能力归在人身上,因为我相信你肯定不只是一个“关门弟子”,把你会的都写上那太多了(◦`~´◦)


面向对象特点:
更符合人类思想习惯的思想
利用对象去实现功能
将复杂事情简单化
程序员的角色由执行者变成了指挥者

针对要解决问题的用户而言,可以把大问题拆解成小问题,分别指挥不同的对象去解决小问题

面向对象开发: 就是不断创建对象,使用对象,指挥对象做事情实现功能
原则:如果有对象,就指挥对象实现功能;如果没有,就创建对象,然后再指挥

面向对象语言特征:
封装(encapsulation)信息隐蔽;
继承(inheritance)代码重用;
多态(polymorphism)灵活、接口统一

Java语言、C++、Python等都是面向对象程序设计语言中的一种,所以都具有这三种特征


类与对象

任何事物都是一个对象(object)
对象由对象组成
每个对象都有属性(静态的描述信息)、行为(动态的功能描述)
具有相似属性和行为的对象可以归为一类

具有相同属性和行为的对象可以抽象为类(引用数据类型的一种)

类的组成:属性(特征)与方法(行为)

类是对象的抽象,对象是类的实例

类定义格式:

[public] class 类名 {
   //属性,可以包含多个
  [权限修饰符] 数据类型 成员变量名;
//构造器,可不写,我们一会讨论
  [权限修饰符] 类名(形参列表) {
       初始化语句
  }
   //行为,可以包含多个
  [权限修饰符] 返回值类型 成员方法名(形参列表) {
       具体功能实现
  }
}

具体案例:

public class Student {
   // 属性: 姓名, 年龄
   // 成员变量
   String name;
   int age;
   // 行为 : 打招呼
   // 成员方法
   public void sayHello() {
       System.out.println("hello");
  }
}

上述案例中,我们定义了学生类Student; 要注意,Student是一种数据类型,是自定义类类型,属于引用类型;

类类型使用时跟int、double、String等类型类似,都必须先定义变量并赋值,才能使用;

类类型和基本数据类型定义变量的方式不同,描述方式也不同,具体如下:

//基本数据类型定义变量
int a = 10; //定义一个int类型变量a
double d = 2.3; //定义一个double类型变量d
//类类型定义变量
Student s1 = new Student(); //实例化一个Student类对象s1
Student s2 = new Student(); //实例化一个Student类对象s2

方法

方法(method):就是完成特定功能的代码块

通过方法的定义和调用,可大大提高代码的复用性与可读性

关于方法,我们其实并不陌生(。・`ω´・),从开始学Java到现在每天都会使用main方法, 具体如下:

public class 测试类名 {
//main方法是一个特殊的方法,其是程序的入口
   public static void main(String[] args) {
System.out.println("hello method");
}
}

定义格式

修饰符 返回值类型 方法名(形式参数列表) {
   方法体语句;
}

方法定义细节:

返回值类型:根据该方法的具体功能而定
如果方法的作用仅做输出,不需要返回数据,则为 void;
如果方法需要计算一个结果并返回,则为结果类型,比如方法要求2个 int数的和,则为 int

方法名:见名知意,驼峰命名法,如果只有一个单词全小写,例: display()、sum()
如果多个单词构成,则第一个单词全小写,后续单词首字母大写,例: sayHello()、getMax()、 findStudentByd()

形式参数列表:根据该方法的具体功能而定,具体案例如下
计算2个int数的和,则 int getSum(int a, int b);
计算3个int数的平均数,则 double getAvg(int x, int y, int z);
遍历一个数组,则 void outArray(int[] array);  

带参方法定义时,参数中的数据类型与变量名都不能缺少,缺少任意一个程序将报错

带参方法定义时,多个参数之间使用逗号(,)分隔

调用

方法调用格式: 方法名(实际参数列表);

public static void main(String[] args) {
//getMax相信大家应该见名知意吧
   // 10,20是实际参数
   int max = getMax(10, 20);
   System.out.println("max: " + max);
   int x = 10;
   int y = 20;
   // a,b是实际参数
   max = getMax(x, y);
   System.out.println("max: " + max);
}
public int getMax(int a, int b) {
   if(a > b)
       return a;
   return b;
}

注意事项: 方法必须先定义,再调用

实际参数列表可以是常量,也可以是变量,也可以是表达式

实际参数类型要匹配形式参数类型(要么完全相同,要么能自动隐式类型 转换)

main方法是入口方法,一个程序中唯一,其可以调用其他普通方法

其他方法不能调用main方法,普通方法可以相互调用

定义一个方法,用来交换两个整形数:

public class Test_Swap {
public static void swap(int x, int y) {
System.out.println("交换前,x: " + x + " y: " + y);
x = x ^ y;
y = x ^ y;
x = x ^ y;
System.out.println("交换后,x: " + x + " y: " + y);
}

public static void main(String[] args) {
int a = 10;
int b = 20;
//调用方法
swap(a,b);

System.out.println("交换后,a: " + a + " b: " + b);
}
}
//输出结果
//交换前,x: 10 y: 20
//交换后,x: 20 y: 10
//交换后,a: 10 b: 20

异或运算的核心特性
交换律:a ^ b = b ^ a
结合律:(a ^ b) ^ c = a ^ (b ^ c)
自反性:a ^ a = 0(一个数与自身异或结果为 0)
恒等性:a ^ 0 = a(一个数与 0 异或结果仍为自身)

利用异或的特性,可以在不创建临时变量的情况下交换两个变量的值

int x = 10, y = 20;

x = x ^ y; // 步骤1:x存储x和y的差异

y = x ^ y; // 步骤2:y = (x^y) ^ y = x(利用自反性和恒等性)

x = x ^ y; // 步骤3:x = (x^y) ^ x = y(完成交换)

重载

同一个类中定义的多个方法,如果同时满足下列条件,则构成方法重载:
多个方法在同一个类中
多个方法具有相同的方法名
多个方法的参数列表不相同(类型不同或者数量不同)
重载跟函数的返回值类型无关

//下面三个求和的方法就构成了重载
public class Test{
   //重载1:求两个int类型数据和的方法
   public static int sum(int a,int b) {
       return a + b;
  }
   //重载2:求两个double类型数据和的方法
   public static double sum(double a,double b) {
       return a + b;
  }
   //重载3:求三个int类型数据和的方法
   public static int sum(int a,int b,int c) {
       return a + b + c;
  }
   
   // 测试
   public static void main(String[] args) {
       //调用方法时,Java虚拟机会通过实际参数的不同来区分同名的方法,从而实现精准调用
       int result = sum(10, 20);
       System.out.println(result);
       double result2 = sum(10.0, 20.0);
       System.out.println(result2);
       int result3 = sum(10, 20, 30);
       System.out.println(result3);
  }
}

方法重载可以提高代码的可读性、简化方法调用、减少命名冲突、提高代码的复用性和提供灵活性。这些优势使得方法重载成为Java编程中常用的技术手段之一。


对象

创建对象格式: 类名 对象名 = new 类名();
调用成员格式: 对象名.成员变量 对象名.成员方法();

public class Test_Student {
   /*
       创建对象的格式:
               类名 对象名 = new 类名();
       调用成员变量的格式:
               对象名.变量名
       调用成员方法的格式:
               对象名.方法名();
    */
//假设我们用上面类中举例那个Student类
   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 = 23;
       // 再次输出对象属性值
       System.out.println(stu.name);  // 张三
       System.out.println(stu.age);   // 23
       // 对象成员方法调用
       // 固定格式:对象名.方法名(实际参数列表);
       stu.sayHello();
       System.out.println(stu); //输出这个对象的结果就是一个地址值
  }

一般Java程序会写两个类:基础类,测试类
基础类就是我们要实现封装出来的那个类
测试类就是包含main方法的类
注意:只能在一个类中定义main方法,其是程序的入口,必须唯一


我们看到,你直接创建一个对象,其内属性都是默认值,这就是成员变量和局部变量的区别,其区别具体有:

定义位置不同
成员变量:类中方法外
局部变量:方法内部或方法声明上(形参列表)
内存中位置不同
成员变量:堆内存
局部变量:栈内存
生命周期不同
成员变量:随着对象的存在而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
初始化值不同
成员变量:有默认初始化值
局部变量:没有默认初始化值,必须先定义,赋值才能使用


类和对象的关系: 通过前面的学习可知,类是一组相关属性和行为的集合,它是对某一种具体事物的抽象描述。也可以把类看作一个模板,我们使用的对象,就是按照这个模板中的定义,来进 行创建的。

类是对一类事物的描述,是抽象的
对象是一类事物的实例,是具体的
类是对象的模板,对象是类的实体

对象内存

单个对象:

public class Test{
public static void main(String[] args) {
   //实例化一个对象
       Student stu = new Student();   
   stu.name = "tom";
   stu.sayHello();
}
}

程序运行过程:
1. 加载Student类:把Student.class文件内容加载到方法区中
2. 加载main方法并运行:整个main方法的代码都被加载到栈区中
3. 创建引用类型变量:在栈空间中开辟一块内存空间,用stu标识
4. 在堆中开辟内存创建对象,并给属性赋上默认初始值
5. 将堆空间对象内存地址值放入stu标识的内存区域中
6. 对象属性赋值:将"tom"和20放入堆空间对象内存区域内
7. 对象方法调用:找到方法区sayHello方法对应的代码,执行
8. main方法继续执行,遇到 } ,程序执行结束

上述案例中,stu是一个引用类型变量,其对应栈区的一块内存区域,其中放的是一个引用值(地址值),通过这个引用值,系统可以找到对象实际开辟的内存空间(堆区),进而进行对象属性操作或方法调用。

多个对象:

public class Test{
public static void main(String[] args) {
//创建对象s1
Student s1 = new Student();
//给对象name属性赋值
   s1.name = "lucy";
//第二个对象
   Student s2 = new Student();
   s2.name = "jack";
   s2.age = 20;
   //来点动作(方法)
   s1.sayHello();
   s2.study();
}
}

对象内存结论:
系统会为每个对象开辟单独的内存空间(堆空间),用来存储对象的属性。
类的成员方法存储在方法区,只保留一份,只要是该类的对象,都可以调用。


封装特性

封装是面向对象三大特征之一,另外两个是继承,多态

封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
其优点如下:
通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性
隐藏代码实现细节,提供公共访问方式,简化操作  

在生活中,电源插座,如果不对电源插座封装,平时用电将会很不安全,容易触电;笔记本开关按钮,用户不需要关心开机关机底层那些复杂的实现细节,只需要知道按按钮可以开关机即可。而这也是封装想达到的结果(。ì _ í。)

修饰限制符

在 Java 中,用于修饰类、属性、方法的访问控制修饰符有 4 个,它们决定了被修饰成员的可访问范围(即其他类能否访问该成员)。这 4 个修饰符按访问权限从大到小排序为:public、protected、default(默认,无关键字)、private。

修饰符同一类中同一包中(不同类)不同包的子类不同包的非子类public

protected

default

private

可访问 可访问 可访问 可访问

不可
不可 不可
不可 不可 不可

封装步骤:
1. 使用private修饰成员变量
2. 提供对应的setXxx()、getXxx()方法,用pubic修饰
3. 具体使用时,借助对象的setXxx方法给属性赋值,getXxx方法获取属性值

//银行账户类
class Account {
//步骤1:private修饰数据成员
private String id; //卡号
private double balance; //金额

//步骤2:定义get|set方法,使用public修饰
public void setBalance(double money) {
balance = money;
}

public double getBalance() {
return balance;
}

public void setId(String accountId) {
id = accountId;
}

public String getId() {
return id;
}
}
public class Test{
public static void main(String[] args) {
//1.实例化对象,使用系统提供的无参构造器
Account acc = new Account();
//2.在类外通过对象访问私有数据成员,编译报错
//acc.id = "001";
//acc.balance = 2000.5;

//步骤3:具体使用时,借助对象的setXxx方法给属性赋值,getXxx方法获取属性值
//3.借助set方法赋值
acc.setId("001");
acc.setBalance(2000.5)
//4.借助get方法获取属性值
System.out.println("卡号: " + acc.getId());
System.out.println("金额: " + acc.getBalance());
}
}

构造方法

上述案例中,我们借助set方法给对象的属性赋值,如果对象的属性特别多,有20个,我们难道每次要调用20个set方法赋初值吗?有没有更好的初始化属性值方案(´`;)?这个时候就要有构造方法

构造方法可以对对象进行初始化操作,即为对象开辟内存空间的时候,给对象的成员成员赋初值。

构造方法格式:

[修饰符] 类名(参数列表) {
   初始化语句s;
}

注意事项:
构造方法一般使用 public 修饰
构造方法没有返回值类型,连 void 都没有
构造方法名和类名相同(区分大小写)
构造方法可以重载

执行时机 :创建对象的时候调用,每创建一次对象,就会执行一次构造方法;不能手动调用构造方法

class Student{
private String name;
private int age;
private String gander;
private String studentId;
public Student(){
}
public Student(String name, int age, String gander, String studentId) {
this.name = name;
this.age = age;
this.gander = gander;
this.studentId = studentId;
}
//get和set方法及其他
}

用户不定义构造方法,系统会提供一个无参构造方法
用户定义构造方法,系统则不再提供无参构造方法
用户不需要也不可以主动调用构造方法,系统会自动调用

可以看到我写了两个构造方法,一个无参一个全参,这正是其可以重载的体现,也是为了应对不同情况(因为用户定义构造方法,系统则不再提供无参构造方法,这个时候直接new Student()是不行的-人-)

对象创建步骤:

public static void main(String[] args) {
Student s = new Student(“zhang”,23);
//不是上面那个,假设一个其他类,大家自己脑补
}

1. 将Student.class文件加载到内存方法区
2. 在main栈帧开辟一块内存,用s标识
3. 在堆中开辟内存创建对象
4. 给属性以默认初始值(null,0)
5. 属性进行显式初始化(如果存在的话,比如 private int age = 10;)
6. 调用构造方法,用("zhang",23)给属性赋值
7. 将堆中对象的内存地址赋值给s,对象创建完成


this关键字

set方法中,如果方法的形式参数名跟类数据成员名相同,如何进行赋值?

我上面的构造方法中已经给出了答案,用了一个this

在类中的普通成员方法中,可以使用this关键字,其表示调用当前方法的对象引用,即哪个对象调用该方法,this就代表哪一个对象。

this关键字用法:
对成员变量和局部变量进行区分–this.数据成员;
调用类中的成员方法–this.成员方法(实际参数列表);
调用类中的其他构造器–this(实际参数列表);

三个愿望一次满足😋:

public class Student {
// 成员变量
private String name;
private int age;
// 无参构造器
public Student() {
}
// 带参构造器(接收姓名)
public Student(String name) {
// 调用本类的另一个带参构造器,this必须写在第一行
this(name, 0);
}
// 全参构造器(接收姓名和年龄)
public Student(String name, int age) {
// 使用this.成员变量区分成员变量和局部变量(参数名与成员变量名相同)
this.name = name; // this.name指向成员变量,name是构造器参数(局部变量)
this.age = age;
}
// 成员方法:设置学生信息
public void setStudentInfo(String name, int age) {
this.name = name;
this.age = age;
// 使用this.成员方法()调用本类的其他成员方法
this.showStudentInfo(); // 等价于 showStudentInfo(),加this更清晰
}
// 成员方法:展示学生信息
public void showStudentInfo() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age);
}
public static void main(String[] args) {
// 测试构造器中的this调用
System.out.println("—–测试无参构造器—–");
Student s1 = new Student();
System.out.println("\\n—–测试单参数构造器—–");
Student s2 = new Student("张三");
System.out.println("\\n—–测试成员方法中的this—–");
Student s3 = new Student();
s3.setStudentInfo("李四", 20); // 调用方法时会触发this.成员变量和this.成员方法
}
}

注意: this(实际参数列表) 必须是构造方法中的第一行有效代码。


总结

Java 面向对象编程的核心,是用 “对象” 封装现实世界的实体,通过属性(特征)和方法(行为)的组合,让代码像现实协作一样解决问题。比起面向过程一步步拆解步骤,它更像在编排一场 “对象大戏”—— 比如网购时,“用户”“APP”“支付系统” 各司其职,通过交互完成流程,而非从头到尾由一串步骤硬拼。

类是对象的 “设计图”,定义了通用属性和方法;对象则是按图造出的 “实体”,带着具体数据参与协作。封装通过 private 隐藏细节、public 暴露接口,让对象更安全;this 关键字理顺变量和方法的归属;构造方法则给对象 “出生” 时就打好基础。这一套思维让复杂系统拆解成可复用的对象模块,既符合人类对实体协作的认知,也让代码更易维护 —— 毕竟,指挥对象做事,总比自己包揽所有步骤更轻松。


希望这篇文章能帮到你,非常感谢各位的点赞、收藏与关注,大家也可以在评论区及时指出错误。
这篇文章我为大家总结了Java面向对象基础,从这篇开始,我会依次为大家介绍static、继承、多态、抽象、接口、内部类等,帮助大家理解面向对象是如何贯穿 Java 语言设计的始终,Java 程序的哪些构建围绕其展开,大家可以关注一手,我会持续更新的<( ̄︶ ̄)/

 

赞(0)
未经允许不得转载:网硕互联帮助中心 » Java 面向对象基础(类与对象)—— Java 的灵魂与骨架
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!