⭕面向过程(POP&)面向对象(OOP):
两者都是一种思想,面向对象是相对于面向过程而言的。
面向过程:强调的是功能行为,以函数为最小单位,考虑怎么做
面向对象:将功能
封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法和原则,如:抽象、分类、继承、聚合、多态等。
- 程序员从面向过程的执行者转化成了面向对象的指挥者
(一)面向对象分析问题的思路和步骤:
- 根据问题需要,选择问题所针对的现实中的实体
- 从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类
- 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把构造成计算机能够识别和处理的数据结构
- 将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具
(二)面向对象的两个要素
- 类 :是对一类事物的描述类的设计,其实就是类的成员的设计。面向对象程序设计的重点是类的设计
- 对象:实际存在的该类事物的每个个体,因而也称为实例(instance)
一、JAVA类及类的成员
(一)JAVA类及类的成员
属性、方法、构造器;代码块、内部类
- 属性:对应类中的成员变量
- 行为:对应类中的成员方法
类和对象的使用(面向对象思想落地的实现):
1.创建类,设计类的成员
2. JAVA类的实例化,即创建类的对象
创建类的对象 = 类的实例化 = 实例化类
3.通过“对象.方法”或“对象.属性”调用对象的结构
Field = 属性 = 成员变量 = 域、字段 Method = (成员)方法 = 函数
public class OOPTest {
public static void main(String[] args) {
//创建person类的对象
Person p1 = new Person();
//Scanner scanner = new Scanner(System.in)
//调用对象的结构:属性、方法
//调用属性:“对象.属性”
p1.name = "Peter";
p1.isMale = true;
p1.age = 20;
System.out.println(p1.name);
System.out.println(p1.isMale);
System.out.println(p1.age);
//调用方法:“对象.方法”
p1.eat();
p1.sleep();
p1.speak("Chinese");
}
}
class Person{
//属性
String name;
int age;
boolean isMale;
//方法(功能)
public void eat()
{
System.out.println("人可以吃");
}
public void sleep()
{
System.out.println("人可以睡觉");
}
public void speak(String language)
{
//括号内定义参数
System.out.println("人可以说话,使用的是:"+language);
}
}
输出结果:
(二)对象的创建和使用
1.如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)
//意味着:如果我们修改一个对象的属性a,则不影响另一个对象属性a的值
Person p2 = new Person();
System.out.println(p2.name); //未赋值时,结果为null
//将p1变量保存的地址值赋给p3,导致p1和P3指向了堆空间中的同一个对象实体。
Person p3 = p1;
System.out.println(p3.name); //Peter
2.内存解析
- 堆(Heap),此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点JAVA虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。new出来的结构(比如:数组、对象.......)加载在堆空间中。补充:对象的属性(非static)加载在堆空间
- 通常所说的栈(Stack),是指虚拟机栈。用于存储局部变量等。局部变量表存放了编译器可知长度的各种基本数据类型(boolean,byte,char,short,int,float,long,double)、对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址)。方法执行完,自动释放
- 方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
JVM内存结构
编译完源程序以后,生成一个或多个字节码文件
我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析
(三)类中属性的使用
属性(成员变量)&局部变量
public void talk(String language) {//language是形参,也是局部变量
System.out.println("我们使用的是"+language+"进行交流");
}
public void food(){
String shiwu = "豆腐脑";//局部变量
System.out.println("我喜欢吃"+shiwu);
}
1.相同点:
1.1 定义变量的格式:数据类型 变量名 = 变量值
1.2 先声明后使用
1.3 变量都有其对应的作用域
2.不同点:
2.1 在类中声明的位置的不同
属性:直接定义在类中的{ }内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
2.2 关于权限修饰符
属性:可以在声明属性时,指明其权限,使用权限修饰符
常用的权限修饰符:
private public 缺省(定义时数据类型前无任何修饰符) protected—>封装性
局部变量:不可以使用权限修饰符
2.3 默认初始化值的情况:
属性:类的属性,根据其类型,都有默认初始化值
整型——byte、short、int、long:0
浮点型——float、double:0.0
字符型——char:0(或'\u0000')
布尔型——boolean:false
引用数据类型(类、数组、接口):null
局部变量:没有初始化值
意味着:在调用局部变量之前,必须要显式赋值
特别地:形参在调用时,赋值即可
2.4 在内存中加载的位置:
属性:加载到堆空间中
局部变量:加载到栈空间中
(四)类中方法的声明和使用
方法:描述类应该具有的功能
1.方法的分类:按照是否有形参及返回值
无返回值 | 有返回值 | |
---|---|---|
无形参 | void方法名( ){} | 返回值的类型 方法名( ){} |
有形参 | void方法名(形参列表){} | 返回值的类型 方法名(形参列表){} |
2.方法的声明:
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
3.说明
3.1 关于权限修饰符
JAVA规定的四种权限修饰符:private public 缺省 protected
3.2 返回值类型 —— 有返回值 & 没有返回值
3.2.1 如果方法有返回值,则必须在方法声明时,指定返回值的类型.
同时,方法中需要使用return关键字来返回指定类型的变量或常量
如果没有返回值,则方法声明时,用void表示(通常不需要return语句,若使 用,只能“return;”,表示结束此方法)
return关键字的使用:
1、使用范围:使用在方法体中
2、作用:结束方法
针对于有返回值类型的方法,使用“return 数据”方法返回所要的数据
3、注意点:return关键字后面不可以声明执行语句
3.2.2 定义方法时该不该有返回值?
1.根据题目要求
2.根据经验,具体问题具体分析
3.3 方法名:属于标识符,遵循标识符的规则规范,“见名知意”。
3.4 形参列表:方法可以声明0个、1个或多个形参
格式 数据类型1 形参1,数据类型2 形参2,........
定义方法时,该不该定义形参?
*题目要求
*根据经验,具体问题具体分析
3.5 方法体:方法功能的体现
3.方法的使用:可以调用当前类的属性或方法(方法内部可以调用方法)
递归方法:方法A中又调用方法A
例题:
public class StudentTest {
public static void main(String[] args) {
Student s[] = new Student[20];
for(int i = 0;i<s.length;i++) {
//给数组元素赋值
s[i] = new Student();
//给student对象的属性赋值
s[i].number = i+1;
s[i].state = (int)(Math.random()*(6-1+1)+1);
s[i].score = (int)(Math.random()*(100-0+1));
}
//遍历学生数组
for(int i = 0;i<s.length;i++) {
System.out.println(s[i].info());
}
System.out.println("****************************************");
//对成绩进行排序:冒泡
for(int i = 0;i<s.length-1;i++) {
for(int j = 0;j<s.length-i-1;j++) {
if(s[j].score>s[j+1].score) {
//如果需要交换顺序,交换的是数组中的元素:Student,而不是交换成绩
Student temp = s[j];
s[j] = s[j+1];
s[j+1] = temp;
}
}
}
for(int i =0;i<s.length;i++) {
System.out.println(s[i].info());
}
}
}
class Student {//不需要加括号!!!!!!
int number;
int state;
int score;
//显示学生信息的方法
public String info() {
return "学号:"+number+",年级:"+state+",成绩:"+score;
}
4.方法的重载:
- 概念:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可
“两同一不同”:同一个类、相同方法名
参数列表不同,参数个数不同,参数类型不同
- 特点:与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类 型)。调用时,根据方法参数列表的不同来区别
- 判断是否是重载:
跟方法的权限修饰符、返回值类型、形参变量名、方法名都没有关系 - 在通过对象调用方法时,如何确定某一个指定的方法 方法名---->参数列表
public class OverLoadTest {
public static void main(String[] args) {
OverLoadTest test = new OverLoadTest();
test.getSum(1, 2);
System.out.println();
}
//如下的4个方法构成了重载
public void getSum(int i,int j) {
System.out.println("1");
}
public void getSum(double d1,double d2) {
System.out.println("2");
}
public void getSum(String s,int i) {
System.out.println("3");
}
public void getSum(int i,String s) {
System.out.println("4");
}
//以下均不能构成重载
// public int getSum(int i,int j) {
// return 0;
// }
// public void getSum(int m,int n) {
// }
// private void getSum(int i,int j) {
// }
}
可变个数的形参:
直接定义能和多个实参相匹配的形参。从而可以用一种更简单的方式,来传递个数可变的实参
* 可变个形参的方法:
* 1.jdk 5.0新增的内容
* 2.具体使用:
* 2.1 可变个数形参的格式,数据类型...变量名
* 2.2 当调用可变个数形参的方法时,传入的参数个数可以是:0个、1个、2个...
* 2.3 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
* 2.4 可变个数形参的方法与本类中方法名相同
* 形参类型也相同的数组之间不构成重载。换句话说,二者不能共存
* 2.5 可变个数形参在方法的形参中必须声明在末尾
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
// test.show(12);
// test.show("hello");
test.show("AA","BB","CC");
test.show(new String[] {"AA","BB","CC","DD"});
}
public void show(int i) {
}
public void show(String s) {
System.out.println("show(String)");
}
public void show(String ... strs) {
System.out.println("show(String ... strs)");
for(int i = 0;i<strs.length;i++) {
System.out.println(strs[i]);
}
}
}
方法参数的值传递机制
方法:必须由其所在类或对象调用才有意义
方法的参数:
形参:方法声明时的参数
实参:方法调用时实际传给形参的参数值
JAVA中方法的参数传递方式只有一种:值传递。即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响:
形参是基本数据类型,将实参基本数据类型变量的“数据值”传递给形参形参是引用数据类型,将实参引用数据类型变量的“地址值”传递给形参
变量赋值:
如果变量是基本数据类型,此时赋值的是变量所保存的数据值
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值
递归方法:一个方法体内调用它自身
- 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制
- 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
import java.util.Scanner;
public class RecursionTest {
public static void main(String[] args) {
RecursionTest test = new RecursionTest();
int m = test.getSum(100);
System.out.println(m);
System.out.println(test.getSum1(100));
System.out.println(test.f(10));
Scanner n = new Scanner(System.in);
int p = n.nextInt();
for(int i = 1;i <= p;i++)
{
System.out.print(test.f1(i)+"\t");
}
}
public int getSum(int n) {
if(n==1) {
return 1;
}else {
return n+getSum(n-1);
}
}
public int getSum1(int n) {//计算1-n所有数字的乘积
if(n==1) {
return 1;
}else {
return n*getSum(n-1);
}
}
//已知有一个数列:f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n)
//其中n是大于0的整数,求f(10)的值
public int f(int n) {
if(n==0) {
return 1;
}
else if(n == 1) {
return 4;
}
else {
return 2*f(n-1)+f(n-2);
}
}
//计算斐波那契数列
public int f1(int n) {
if(n==1||n==2) {
return 1;
}
else {}
return f1(n-1)+f1(n-2);
}
}
匿名对象的使用:
1.创建对象时,没有显式的赋给一个变量名,即匿名对象
2.匿名对象只能调用一次
new Phone().sendEmail();
new Phone().playGame();
new Phone().price = 1999;
new Phone().showPrice();//该四行代码new了4个对象