面向对象思想
概述:Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具备自己的属性和行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。 它区别于面向过程思想,强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。程序员从面向过程的执行者转化成了面向对象的指挥者。 “万事万物皆对象”。
特点
- 面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。面向对象的语言中,包含了三大基本特征,即封装、继承和多态。
面向过程(POP) 与 面向对象(OOP)
- 二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的 是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对 象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
- 面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如 抽象、分类、继承、聚合、多态等。
面向对象的三大特征
- 封装 (Encapsulation)
- 继承 (Inheritance)
- 多态 (Polymorphism)
类(Class)和对象(Object)是面向对象的核心概念。
- 类是对一类事物的描述,是抽象的、概念上的定义
- 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。
- 可以理解为:类 = 抽象概念的人;对象 = 实实在在的某个人
- 面向对象程序设计的重点是类的设计
- 类的设计,其实就是类的成员的设计
- 属 性:对应类中的成员变量
- 行 为:对应类中的成员方法
面向对象分析方法分析问题的思路和步骤:
- 根据问题需要,选择问题所针对的现实世界中的实体。
- 从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。
- 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序 语言,把类构造成计算机能够识别和处理的数据结构。
- 将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。
什么是类
类:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
现实中,描述一类事物:
- 属性:就是该事物的状态信息。
- 行为:就是该事物能够做什么。
举例:猫类
- 属性:名字、体重、年龄、颜色。
- 行为:走、跑、叫
什么是对象
对象:是一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为。
现实中,一类事物的一个实例:一只小猫。
举例:一只小猫。
- 属性:tom、5kg、2 years、yellow。
- 行为:溜墙根走、蹦跶的跑、喵喵叫。
类与对象的关系
- 类是对一类事物的描述,是抽象的。
- 对象是一类事物的实例,是具体的。
- 类是对象的模板,对象是类的实体。
类的语法格式
格式详解:
- 定义类:就是定义类的成员,包括成员变量和成员方法。
- 成员变量:在类中,方法外的变量。
- 成员方法:成员方法之中不能包括static关键字
创建Java自定义类
- 定义类(考虑修饰符、类名)
- 编写类的属性(考虑修饰符、属性类型、属性名、初始化值)
- 编写类的方法(考虑修饰符、返回值类型、方法名、形参等)
对象的创建和使用
通常情况下,一个类并不能直接使用,需要根据类创建一个对象,才能使用。
步骤如下
1. 导包:也就是指出需要使用的类,在什么位置。
- 格式: import 包名称.类名称;
注意:
- 对于和当前类属于同一个包的情况,可以省略导包语句不写。
- java.lang包下的所有类无需导入。
2. 创建
- 格式:类名称 对象名 = new 类名称();
3. 使用成员:
- 使用成员变量 格式:对象名.成员变量名
- 使用成员方法 格式:对象名.成员方法名(参数)
注意事项:
- 如果成员变量没有进行赋值,那么将会有一个默认值如下图所示。
举例:
/*
* 一、设计类,其实就是设计类的成员
*
* 属性 = 成员变量 = field = 域、字段
* 方法 = 成员方法 = 函数 = method
*
* 创建类的对象 = 类的实例化 = 实例化类
*
* 二、类和对象的使用(面向对象思想落地的实现):
* 1.创建类,设计类的成员
* 2.创建类的对象
* 3.通过“对象.属性”或“对象.方法”调用对象的结构
*
* 三、如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)
* 意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。
*
*/
//测试类
public class PersonTest {
public static void main(String[] args) {
//2. 创建Person类的对象
Person p1 = new Person();
//给属性赋值
p1.name = "Tom";
p1.isMale = true;
//调用属性:“对象.属性”
System.out.println(p1.name);//Tom
//调用方法:“对象.方法”
p1.eat();//人可以吃饭
p1.sleep();//人可以睡觉
p1.talk("Chinese");//人可以说话,使用的是:Chinese
//*******************************
Person p2 = new Person();
System.out.println(p2.name);//null
System.out.println(p2.isMale);//false
//*******************************
//将p1变量保存的对象地址值赋给p3,导致p1和p3指向了堆空间中的同一个对象实体。
Person p3 = p1;
System.out.println(p3.name);//Tom
p3.age = 10;
System.out.println(p1.age);//10
}
}
//1.创建类,设计类的成员
class Person {
//属性
String name;
int age = 1;
boolean isMale;
//方法
public void eat() {
System.out.println("人可以吃饭");
}
public void sleep() {
System.out.println("人可以睡觉");
}
public void talk(String language) {
System.out.println("人可以说话,使用的是:" + language);
}
}
结论:
类的访问机制:
- 在一个类中的访问机制:类中的方法可以直接访问类中的成员变量。 (例外:static方法访问非static,编译不通过。)
- 在不同类中的访问机制:先创建要访问类的对象,再用对象访问类中 定义的成员。
对象的创建和使用:内存解析
- 堆(Heap),此内存区域的唯一目的 就是存放对象实例,几乎所有的对象 实例都在这里分配内存。这一点在 Java虚拟机规范中的描述是:所有的 对象实例以及数组都要在堆上分配。
- 通常所说的栈(Stack),是指虚拟机栈。虚拟机栈用于存储局部变量等。 局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、 char 、 short 、 int 、 float 、 long 、 double)、对象引用(reference类型, 它不等同于对象本身,是对象在堆内 存的首地址)。 方法执行完,自动释 放。
- 方法区(Method Area),用于存储已 被虚拟机加载的类信息、常量、静态 变量、即时编译器编译后的代码等数
一个对象,调用一个方法内存图
通过上图,我们可以理解,在栈内存中运行的方法,遵循"先进后出,后进先出"的原则。变量p指向堆内存中的空间,寻找方法信息,去执行该方法。但是,这里依然有问题存在。创建多个对象时,如果每个对象内部都保存一份方法信息,这就非常浪费内存了,因为所有对象的方法信息都是一样的。那么如何解决这个问题呢?请看如下图解
两个对象,调用同一方法内存图
对象调用方法时,根据对象中方法标记(地址值),去类中寻找方法信息。这样哪怕是多个对象,方法信息只保存一份,节约内存空间。
一个引用,作为参数传递到方法中内存图
匿名对象
概念:创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。虽然是创建对象的简化写法,但是应用场景非常有限。匿名对象 :没有变量名的对象。
格式:
1. 简化代码,创建匿名对象直接调用方法,没有变量名。
new Scanner(System.in).nextInt();
2. 匿名对象可以作为方法的参数和返回值
- 作为方法的参数
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
/*
普通方式
Scanner sc = new Scanner(System.in);
input(sc);
*/
//匿名对象作为方法接收的参数
input(new Scanner(System.in));
}
public static void input(Scanner sc) {
System.out.println(sc);
}
}
- 作为方法的返回值
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
Scanner sc = getScanner();
}
public static Scanner getScanner() {
/*
普通方式
Scanner sc = new Scanner(System.in);
return sc;
*/
//匿名对象作为方法返回值
return new Scanner(System.in);
}
}
注意事项:
- 匿名对象只能使用唯一的一次,下次再用不得不再创建一个新对象。如果确定有一个对象只需要使用唯一的一次,就可以用匿名对象。
类的成员之一:属性
- 修饰符 数据类型 属性名 = 初始化值 ;
- 修饰符:常用的权限修饰符有:private、缺省、protected、public 其他修饰符:static、final .....
- 数据类型 :任何基本数据类型(如int、Boolean) 或 任何引用数据类型。
- 属性名 :属于标识符,符合命名规则和规范即可。
- 当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。除了基本数据类型之外的变量类型都是引用类型。
package demo01;
public class Demo {
//声明private修饰的int类型的变量 age
private int age;
//声明public修饰的String变量 name
public String name = "Lila";
}
变量的分类:成员变量与局部变量
成员变量(属性)和局部变量的区别?
举例
public class UserTest {
public static void main(String[] args) {
User u1 = new User();
System.out.println(u1.name);//张三
System.out.println(u1.age);//0
System.out.println(u1.isMale);//false
u1.talk("韩语");//我们使用韩语进行交流
u1.eat();//北方人喜欢吃:烙饼
}
}
class User {
//属性(或成员变量)
String name = "张三";
public int age;
boolean isMale;
public void talk(String language) {//language:形参,也是局部变量
System.out.println("我们使用" + language + "进行交流");
}
public void eat() {
String food = "烙饼";//局部变量
System.out.println("北方人喜欢吃:" + food);
}
}
类的成员之二:方 法(method)
什么是方法(method、函数):
- 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中 也称为函数或过程。
- 将功能封装为方法的目的是,可以实现代码重用,简化代码
- Java里的方法不能独立存在,所有的方法必须定义在类里。
方法的分类:按照是否有形参及返回值
举例:
public class MethodTest {
private int age;
public int getAge() { //声明方法getAge()
return age;
}
public void setAge(int i) { //声明方法setAge
age = i; //将参数i的值赋给类的成员变量age
}
}
注 意:
- 方法被调用一次,就会执行一次
- 定义方法时,方法的结果应该返回给调用者,交由调用者处理。
- 方法中只能调用方法或属性,不可以在方法内部定义方法。
详解方法的定义格式:
权限修饰符:默认方法的权限修饰符先都使用public。Java规定的4种权限修饰符:private、public、缺省、protected
返回值类型:
- 方法有返回值:则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用return关键字来返回指定类型的变量或常量:“return 数据”。
- 方法没有返回值:则方法声明时,使用void来表示。通常,没有返回值的方法中,就不需要使用return.但是,如果使用的话,只能“return;”表示结束此方法的意思。
方法名:属于标识符,遵循标识符的规则和规范,“见名知意”
形参列表: 方法可以声明0个,1个,或多个形参。格式:数据类型1 形参1,数据类型2 形参2,...
方法体:方法功能的体现。
方法的调用
- 方法通过方法名被调用,且只有被调用才会执行
调用方法的三种形式
- 直接调用:直接写方法名调用
public class Demo {
public static void main(String[] args) {
//直接调用
print();
}
public static void print() {
System.out.println("方法被调用"); //方法被调用
}
}
赋值调用:调用方法,在方法前面定义变量,接收方法返回值
public class Demo {
public static void main(String[] args) {
//赋值调用
int sum = getSum(5, 6);
System.out.println(sum); //11
}
public static int getSum(int a, int b) {
return a + b;
}
}
输出语句调用:在输出语句中调用方法, System.out.println(方法名(参数列表)) 。
public class Demo {
public static void main(String[] args) {
//输出语句调用:
System.out.println(getSum(5, 6));//11
}
public static int getSum(int a, int b) {
return a + b;
}
}
注意事项
- 对于有返回值的方法,可以使用单独调用、打印调用或者赋值调用。
- 但是对于无返回值的方法,只能使用单独调用,不能使用打印调用或者赋值调用。
使用方法的时候,注意事项:
- 方法应该定义在类当中,但是不能在方法当中再定义方法。不能嵌套。方法中只能调用方法或属性。
- 方法定义的前后顺序无所谓。
- 方法定义之后不会执行,如果希望执行,一定要调用:单独调用、打印调用、赋值调用。
- 如果方法有返回值,那么必须写上“return 返回值;”,不能没有。
- return后面的返回值数据,必须和方法的返回值类型,对应起来。
- 对于一个void没有返回值的方法,不能写return后面的返回值,只能写return自己。
- 对于void方法当中最后一行的return可以省略不写。
- 一个方法当中可以有多个return语句,但是必须保证同时只有一个会被执行到,两个return不能连写。
练习题
定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
- 问题一:打印出3年级(state值为3)的学生信息。
- 问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
提示:
- 生成随机数:Math.random(),返回值类型double;
- 四舍五入取整:Math.round(double d),返回值类型long。
定义Student类
class Student{
int number;//学号
int state;//年级
int score;//成绩
//显示学生信息的方法
public String info(){
return "学号:" + number + ",年级:" + state + ",成绩:" + score;
}
}
定义测试类
public class StudentTest {
public static void main(String[] args) {
//声明Student类型的数组
Student[] stus = new Student[20];
for(int i = 0;i < stus.length;i++){
//给数组元素赋值
stus[i] = new Student();
//给Student对象的属性赋值
stus[i].number = (i + 1);
//年级:[1,6]
stus[i].state = (int)(Math.random() * (6 - 1 + 1) + 1);
//成绩:[0,100]
stus[i].score = (int)(Math.random() * (100 - 0 + 1));
}
//问题一:打印出3年级(state值为3)的学生信息。
for(int i = 0;i <stus.length;i++){
if(stus[i].state == 3){
System.out.println(stus[i].info());
}
}
//问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
for(int i = 0;i < stus.length - 1;i++){
for(int j = 0;j < stus.length - 1 - i;j++){
if(stus[j].score > stus[j + 1].score){
//如果需要换序,交换的是数组的元素:Student对象!!!
Student temp = stus[j];
stus[j] = stus[j + 1];
stus[j + 1] = temp;
}
}
}
//遍历学生数组
for(int i = 0;i <stus.length;i++){
System.out.println(stus[i].info());
}
}
}
理解“万事万物皆对象”
- 在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构
- 涉及到Java语言与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。