面向对象思想概述
Java语言是一种面向对象的程序设计语言,而面向对象思想(OOP)是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具备自己的属性和行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。 它区别于面向过程思想(POP),强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。面向对象的三大特征:封装 (Encapsulation) 继承 (Inheritance) 多态 (Polymorphism)
面向对象与面向过程的区别
- 面向过程:以函数(方法)为最小单位,数据独立于函数之外,以过程,步骤为主,考虑怎么做...。注重的是过程,必须清楚每一个步骤,按照步骤一步一步去实现
- 面向对象:以类/对象为最小单位,类包括:数据+方法,以对象(谁)为主,考虑谁来做,谁能做 。注重的是对象,无须清楚每一个步骤,只需要使用对象调用行为来完成需求
面向对象仍然包含面向过程,只不过关注点变了,关注谁来做
类和对象的关系
类的概述
类是用来描述一类具有共同属性和行为事物的统称。所以其实类在客观世界里是不存在的,是抽象的,只是用来描述数据信息的。
类的组成
- 类的属性:就是该事物的状态信息。例如:手机类的属性,品牌、价格...
- 类的行为:就是该事物能够做什么。例如:手机类的行为,打电话、发短信...
对象的概述
对象是类的一个实例,具体存在的,看得见摸得着的,并且具备该类事物的属性和行为
- 对象的属性:对象的属性具有特定的值。例如:手机对象的属性,品牌华为、价格1999。 对象的属性具体的值,类中的属性没有具体的值
- 对象的行为:对象可以操作的行为。例如:手机对象的行为,使用打电话功能,使用发短信功能。对象可以使用行为
类和对象的关系
- 类是对一类具有共同属性和行为的事物的统称,是抽象的
- 对象是一类事物的具体实例,看得见,摸的着的,真实存在的实体,是具体的
- 类是对象的抽象,对象是类的实体
- 对象是根据类来创建的,类中有什么,对象就有什么,可以把类看成是对象的数据类型
类的定义和对象的创建
现实世界的一类事物:
- 属性:事物的状态信息。
- 行为:事物能够做什么。
Java中用class描述事物也是如此:
- 成员变量:对应事物的属性
- 成员方法:对应事物的行为
定义类:就是定义类的成员,包括成员变量和成员方法。
类的定义
成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
成员方法:和以前写的main方法格式类似。只不过功能和形式更丰富了。在类中,方法外。
Java自定义类,步骤一般如下所示:
- 定义类(考虑修饰符、类名)
- 编写类的属性(考虑修饰符、属性类型、属性名、初始化值)
- 编写类的方法(考虑修饰符、返回值类型、方法名、形参等)
代码示例
public class Student {
// 成员变量: 属性
/**
* 姓名
*/
String name;
/**
* 年龄
*/
int age;
// 成员方法: 行为
/**
* 学习的功能
*/
public void study(){
System.out.println("学生正在学习Java...");
}
/**
* 做作业的功能
*/
public void doHomeWork(){
System.out.println("学生正在做作业敲代码...");
}
}
对象的创建
java类的实例化,即创建类的对象,格式如下
现在我们创建Student类的对象
public class Test {
public static void main(String[] args) {
// 需求:首先定义一个学生类,然后定义一个学生测试类,在学生测试类中通过对象完成成员变量和成员方法的使用
// 创建学生对象
Student stu = new Student();
// 访问成员变量
stu.name = "冰冰";
stu.age = 18;
System.out.println(stu.name + "," + stu.age);// 冰冰,18
// 访问成员方法
stu.study();
stu.doHomeWork();
}
}
总结:
- 使用new +构造器创建一个新的对象
- 使用“对象名.对象成员”的方式访问对象成员(包括属性和方法)
- 同一个类中的方法可以直接访问类中的成员变量。(特殊情况:static方法访问非static,编译不通过。)
- 在不同类中:要先创建要访问类的对象,再用对象访问类中定义的成员。
类的成员之一:成员变量(属性)
成员变量的分类
- 实例变量:没有static修饰,也叫对象属性,属于某个对象的,通过对象来使用
- 类变量:有static修饰,也叫类变量,属于整个类的,不是属于某个实例
成员变量(属性)和局部变量的区别
成员变量(属性)和局部变量的区别 有很多,主要有以下几点
- 类中位置不同:成员变量(类中方法外)局部变量(方法内部或方法声明上)
- 内存中位置不同:成员变量(堆内存)局部变量(栈内存)
- 生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,随着方法的调用完毕而消失)
- 初始化值不同:成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
如何声明成员变量
例如:声明一个中国人的类
class Chinese{
static String country;
String name;
char gender = '男';//显式赋值
}
如何在类外面访问类变量
如何在类外面访问实例变量
代码示例
class Chinese{
static String country;//静态变量,未赋值
String name;//类变量,未赋值
char gender = '男';//类变量,显示赋值
}
public class TestChinese {
public static void main(String[] args) {
//类名.静态成员变量
System.out.println(Chinese.country);
// System.out.println(Chinese.name);错误,非静态成员变量必须通过对象.进行访问
//创建对象
Chinese c1 = new Chinese();
//对象名.非静态成员变量
System.out.println(c1.name);
//静态的成员变量也可以通过对象.进行访问
//对象名.非静态成员变量
System.out.println(c1.country);
System.out.println(c1.gender);
}
}
成员变量有默认值
代码示例
class Student {
/**
* 姓名
*/
String name;
/**
* 年龄
*/
int age;
/**
* 分数
*/
double score;
char c;
}
public class Test {
public static void main(String[] args) {
/*
成员变量的默认值:
整数类型: 默认值是0
小数类型: 默认值是0.0
布尔类型: 默认值是false
字符类型: 默认值是不可见字符 '\u0000'
引用类型: 默认值是null
*/
// 创建Student对象
Student stu = new Student();
// 访问成员变量
System.out.println(stu.name);// null
System.out.println(stu.age);// 0
System.out.println(stu.score);// 0.0
System.out.println("="+stu.c+"=");
}
}
类变量的值是所有对象共享的,而实例变量的值是每个对象独立的
class Chinese {
static String country;
String name;
char gender;
}
public class TestChinese {
public static void main(String[] args) {
//创建对象
Chinese c1 = new Chinese();
Chinese c2 = new Chinese();
//实例变量的值是每个对象独立的
c1.name = "张三";
c2.name = "李四";
c1.gender='男';
c2.gender = '女';
//给类变量赋值,该类的所有对象,皆共享
Chinese.country = "中国";//推荐
System.out.println(c1.country + "\t" + c1.name + "\t" + c1.gender);//中国 张三 男
System.out.println(c2.country + "\t" + c2.name + "\t" + c2.gender);//中国 李四 女
}
}
类的成员之二:方 法
方法:就是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集。Java里的方法不能独立存在,所有的方法必须定义在类里。
成员方法分为两类:
- 实例方法:没有static修饰的方法,必须通过实例对象来调用。
- 静态方法:有static修饰的方法,也叫类方法,可以由类名来调用。
如何声明方法
方法声明的位置必须在类中方法外
语法格式:格式详解:
- 修饰符: 对方法的访问权限,状态进行修饰的关键字,例如:public static
- 返回值类型: 表示方法运行的结果的数据类型,可以是任意的数据类型。方法执行后将结果返回到调用者。例如:如果方法内部返回整数数字100,返回值类型写为int。方法内部没有返回值返还给调用者,返回值类型必须固定写为void
- 方法名:给方法起一个名字(符合标识符的命名规范,小驼峰原则(第一个单词首字母小写,其它单词首字母大写)),见名知意,能准确代表该方法功能的名字
- 参数列表:方法内部需要用到其他方法中的数据,需要通过参数传递的形式将数据传递过来,可以是基本数据类型、引用数据类型、也可以没有参数,什么都不写。方法没有参数,不用写参数列表,但是必须保留小括号()
- 功能代码: 完成特殊功能的一行/多行代码
-
return 返回值:结束方法,并将方法的结果返回去,
- 如果返回值类型不是void,方法体中必须保证一定有return 返回值;语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容
- 如果返回值类型为void时,return 后面不用跟返回值,甚至也可以没有return语句。
- return语句后面就不能再写其他代码了,否则会报错:Unreachable code
代码示例
public class Test {
public static void main(String[] args) {
System.out.println("main...start...");
//调用方法: 传递的是常量
int result = getMax(100, 200);
System.out.println("100和200的最大值: " + result);
//调用方法方法: 传递变量
int a = 10, b = 20;
int max = getMax(a, b);
System.out.println(a + "和" + b + "的最大值: " + max);
System.out.println("main...end...");
}
//设计一个方法可以获取两个int数的较大值,数据来自于参数
public static int getMax(int a, int b) {
int max = (a > b) ? a : b;
return max;//结束方法,并且把max中的数据,返还给方法的调用处/者
}
}
图解分析
如何在其他类中调用方法
实例方法
类方法
形式参数和实际参数的区别
- 形参:在定义方法时方法名后面括号中声明的变量称为形式参数(简称形参)即形参出现在方法定义时。
- 实参:调用方法时方法名后面括号中的使用的值/变量/表达式称为实际参数(简称实参)即实参出现在方法调用时。
总结:
- 调用时,需要传“实参”,实参的个数、类型、顺序顺序要与形参列表一一对应。如果方法没有形参,就不需要也不能传实参。
- 调用时,如果方法有返回值,可以接受或处理返回值结果,当然也可以不接收,那么此时返回值就丢失了。如果方法的返回值类型是void,不需要也不能接收和处理返回值结果。
代码示例
/*
形式参数和实际参数的区别
1.形式参数:
(1)概念: 定义方法时()中定义的参数(定义变量),叫做形式参数
(2)特点:
a.定义形式参数的时候,是没有值的
b.当调用该方法时,形式参数才会有值
2.实际参数:
(1)概念: 调用方法时()中给出的参数(常量/变量),叫做实际参数
(2)特点:
a.调用方法时,()中写的数据(常量/变量)
b.必须要有值才可以
*/
public class Demo {
public static void main(String[] args) {
System.out.println("main...start...");
/*
2.实际参数:
(1)概念: 调用方法时()中给出的参数(常量/变量),叫做实际参数
(2)特点:
a.调用方法时,()中写的数据(常量/变量)
b.必须要有值才可以
*/
//调用方法: 传递常量
printSum(10,20);//10,20叫做实际参数
//调用方法: 传递变量
int m = 100,n = 200;
printSum(m,n);//m,n叫做实际参数
System.out.println("main...end...");
}
/*
1.形式参数:
(1)概念: 定义方法时()中定义的参数(定义变量),叫做形式参数
(2)特点:
a.定义形式参数的时候,是没有值的
b.当调用该方法时,形式参数才会有值
*/
//定义方法,打印2个int数字之和
public static void printSum(int a, int b) {
int sum = a + b;
System.out.println("和: "+sum);
return ;//结束方法,返回到方法的调用处,注意没有带回任何数据
}
}
方法的注意事项
- 方法不能嵌套定义(在定义方法的内部又定义了其它方法),可以调用其它方法
- 方法可以嵌套调用
- 返回值类型,必须要和 return 语句返回的数据的类型要匹配,否则编译失败 。
- 不能在 return 后面写代码, return 意味着方法结束,所有后面的代码永远不会执行,属于无效代码。
- void表示无返回值,可以省略return,也可以单独的书写return,后面不加数据
- 类,变量,方法等都要先声明后使用。
- 方法不调用不执行,调用一次执行一次,每次调用会在栈中有一个入栈动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值,当方法执行结束后,会释放该内存,称为出栈,如果方法有返回值,就会把结果返回调用处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。
方法的参数传递机制
参数传递:可以理解当我们要调用一个方法时,我们会把指定的数值,传递给方法中的参数这样方法中的参数就拥有了这个指定的值,可以使用该值,在方法中运算了。这种传递方式,我们称为参数传递。
基本类型作为方法参数传递
public class Test1 {
/*
方法参数传递为基本数据类型 :
传入方法中的, 是具体的数值.
*/
public static void main(String[] args) {
int number = 100;
System.out.println("调用change方法前:" + number);
change(number);
System.out.println("调用change方法后:" + number);
}
public static void change(int number) {
number = 200;
}
}
结论:
- 基本数据类型的参数,形式参数的改变,不影响实际参数
结论依据:
- 每个方法在栈内存中,都会有独立的栈空间,方法运行结束后就会弹栈消失
引用类型作为方法参数传递
public class Test {
/*
方法参数传递为引用数据类型 :
传入方法中的, 是内存地址.
*/
public static void main(String[] args) {
int[] arr = {10, 20, 30};
System.out.println("调用change方法前:" + arr[1]);
change(arr);
System.out.println("调用change方法后:" + arr[1]);
}
public static void change(int[] arr) {
arr[1] = 200;
}
}
结论:
- 对于引用类型的参数,形式参数的改变,影响实际参数的值
结论依据:
- 引用数据类型的传参,传入的是地址值,内存中会造成两个引用指向同一个内存的效果,所以即使方法弹栈,堆内存中的数据也已经是改变后的结果
方法重载
方法重载:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
方法重载中参数列表不同有哪些情况
- 参数数量不同
- 参数类型不同
- 多个类型,顺序不同
方法重载与哪些因素无关
- 与参数的名称无关
- 与返回值类型无关
- 与修饰符无关
重载方法调用:JVM通过方法的参数列表,调用不同的方法。
- 根据名称找到对应的方法
- 根据参数的数量找到对应的方法
- 根据参数的类型确定最终要调用的方法(首先: 做类型完全匹配 其次: 完全匹配的找不到,再做自动类型提升的匹配)
代码示例
// 使用方法重载的思想,设计比较两个数据是否相等的方法,兼容全整数类型(byte,short,int,long)
public class Test {
public static void main(String[] args) {
//5.分别调用以上四个方法
System.out.println(compare(10,20));
System.out.println(compare((byte)10,(byte)20));
System.out.println(compare((short)10,(short)20));
System.out.println(compare(10L,20L));
}
//1.使用方法重载的思想,定义比较两个byte数据的方法compare
public static boolean compare(byte a, byte b) {
System.out.println("...两个byte...");
return a == b;
}
//2.使用方法重载的思想,定义比较两个short数据的方法compare
public static boolean compare(short a, short b) {
System.out.println("...两个short...");
return a == b;
}
//3.使用方法重载的思想,定义比较两个int数据的方法compare
public static boolean compare(int a, int b) {
System.out.println("...两个int...");
return a == b;
}
//4.使用方法重载的思想,定义比较两个long数据的方法compare
public static boolean compare(long a, long b) {
System.out.println("...两个long...");
return a == b;
}
}
对象内存图
内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来。我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。Java虚拟机要运行程序,必须要对内存进行空间的分配和管理,每一片区域都有特定的处理数据方式和内存管理方式。
详解
查看下面代码
class Student {
String name;
int age;
public void study(){
System.out.println("学生正在学习Java...");
}
public void doHomeWork(){
System.out.println("学生正在做作业敲代码...");
}
}
public class Test {
public static void main(String[] args) {
// 创建Student对象
Student stu = new Student();
System.out.println(stu);// 十六进制数地址值
// 访问成员变量
stu.name = "冰冰";
stu.age = 18;
System.out.println(stu.name+","+stu.age);
// 访问成员方法
stu.study();
stu.doHomeWork();
}
}
内存分析:如下图所示
结论:
- 只要创建对象,就会在堆区开辟一块空间(凡是new就会在堆区开辟一块新的空间),对象和对象之间的关系是相互独立的
- 只要调用方法,就会在栈区开辟一块空间,用来执行该方法
- 多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自对象的内存区域中,成员方法多个对象共用的一份
- 当多个对象的引用指向同一个内存空间(变量所记录的地址值是一样的) 。只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。